// The subtype is a pointer, the supertype could be anything. static bool is_pointer_subtype(ast_t* sub, ast_t* super) { switch(ast_id(super)) { case TK_NOMINAL: { // Must be a Pointer, and the type argument must be the same. return is_pointer(super) && is_eq_typeargs(sub, super) && is_sub_cap_and_ephemeral(sub, super); } case TK_TYPEPARAMREF: { // We must be a subtype of the constraint. ast_t* def = (ast_t*)ast_data(super); ast_t* constraint = ast_childidx(def, 1); return is_pointer_subtype(sub, constraint); } case TK_ARROW: { // We must be a subtype of the lower bounds. ast_t* lower = viewpoint_lower(super); bool ok = is_pointer_subtype(sub, lower); ast_free_unattached(lower); return ok; } default: {} } return false; }
static matchtype_t is_arrow_match_x(ast_t* operand, ast_t* pattern, pass_opt_t* opt) { // upperbound(this->T1) match T2 // --- // (this->T1) match T2 // lowerbound(T1->T2) match T3 // --- // (T1->T2) match T3 ast_t* operand_view; AST_GET_CHILDREN(operand, left, right); if(ast_id(left) == TK_THISTYPE) operand_view = viewpoint_upper(operand); else operand_view = viewpoint_lower(operand); if(operand_view == NULL) return MATCHTYPE_DENY; matchtype_t ok = is_x_match_x(operand_view, pattern, opt); ast_free_unattached(operand_view); return ok; }
// The subtype is an arrow, the supertype could be anything. static bool is_arrow_subtype(ast_t* sub, ast_t* super) { switch(ast_id(super)) { case TK_UNIONTYPE: // A->B <: (C | D) if(is_subtype_union(sub, super)) return true; break; case TK_ISECTTYPE: // A->B <: (C & D) if(is_subtype_isect(sub, super)) return true; break; case TK_TYPEPARAMREF: { // A->B <: C ast_t* right = ast_child(sub); switch(ast_id(right)) { case TK_THISTYPE: case TK_BOXTYPE: break; default: if(is_eqtype(right, super)) { // C->B <: C // If we are adapted by the supertype, we are a subtype if our // lower bounds is a subtype of super. ast_t* lower = viewpoint_lower(sub); bool ok = is_subtype(lower, super); ast_free_unattached(lower); return ok; } break; } break; } case TK_ARROW: // A->B <: C->D if(is_arrow_sub_arrow(sub, super)) return true; break; default: {} } // Otherwise, we are a subtype if our upper bounds is a subtype of super. ast_t* upper = viewpoint_upper(sub); bool ok = is_subtype(upper, super); ast_free_unattached(upper); return ok; }
ast_t* viewpoint_lower(ast_t* type) { switch(ast_id(type)) { case TK_TUPLETYPE: case TK_UNIONTYPE: case TK_ISECTTYPE: { // Adapt all elements. ast_t* r_type = ast_from(type, ast_id(type)); ast_t* child = ast_child(type); while(child != NULL) { ast_append(r_type, viewpoint_lower(child)); child = ast_sibling(child); } return r_type; } case TK_NOMINAL: return viewpoint_lower_for_type(type, 3); case TK_TYPEPARAMREF: return viewpoint_lower_for_type(type, 1); case TK_ARROW: { AST_GET_CHILDREN(type, left, right); // If left is a boxtype, right's lower bounds is its actual type. if(ast_id(left) == TK_BOXTYPE) return viewpoint_cap(TK_BOX, TK_NONE, right); // If left is a thistype or a typeparamref type, right's lower bounds is // calculated on its own. return viewpoint_lower(right); } default: {} } return NULL; }
// The subtype is a nominal, typeparamref or tuple, the super type is an arrow. static bool is_subtype_arrow(ast_t* sub, ast_t* super) { // Must be a subtype of the lower bounds. ast_t* lower = viewpoint_lower(super); bool ok = is_subtype(sub, lower); ast_free_unattached(lower); return ok; }
static bool is_arrow_compat_arrow(ast_t* a, ast_t* b) { // S = this | A {#read, #send, #share, #any} // K = N k | A {iso, trn, ref, val, box, tag} | K->K | (empty) // L = S | K // T = N k | A k | L->T // // forall K' in S . K->S->T1 {S |-> K'} ~ T2 {S |-> K'} // --- // K->S->T1 ~ T2 ast_t* r_a; ast_t* r_b; if(viewpoint_reifypair(a, b, &r_a, &r_b)) { bool ok = is_compat_type(r_a, r_b); ast_free_unattached(r_a); ast_free_unattached(r_b); return ok; } // No elements need reification. // // lowerbound(T1->T2) ~ lowerbound(T3->T4) // --- // T1->T2 ~ T3->T4 r_a = viewpoint_lower(a); if(r_a == NULL) return false; r_b = viewpoint_lower(b); if(r_b == NULL) { ast_free_unattached(r_a); return false; } bool ok = is_compat_type(r_a, r_b); ast_free_unattached(r_a); ast_free_unattached(r_b); return ok; }
static matchtype_t is_arrow_match_nominal(ast_t* operand, ast_t* pattern) { // lowerbound(T1->T2) match T3 // --- // (T1->T2) match T3 ast_t* operand_lower = viewpoint_lower(operand); matchtype_t ok = is_matchtype(operand_lower, pattern); ast_free_unattached(operand_lower); return ok; }
static matchtype_t could_subtype_with_arrow(ast_t* sub, ast_t* super) { // Check the lower bounds of super. ast_t* lower = viewpoint_lower(super); matchtype_t ok = could_subtype(sub, lower); if(lower != sub) ast_free_unattached(lower); return ok; }
static bool is_arrow_compat_nominal(ast_t* a, ast_t* b) { // lowerbound(T1->T2) ~ N k // --- // T1->T2 ~ N k ast_t* a_lower = viewpoint_lower(a); if(a == NULL) return false; bool ok = is_compat_type(a_lower, b); ast_free_unattached(a_lower); return ok; }
ast_t* viewpoint_cap(token_id cap, token_id eph, ast_t* type) { if(cap == TK_TAG) return NULL; switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { // Adapt all elements. ast_t* r_type = ast_from(type, ast_id(type)); ast_t* child = ast_child(type); while(child != NULL) { ast_append(r_type, viewpoint_cap(cap, eph, child)); child = ast_sibling(child); } return r_type; } case TK_NOMINAL: return viewpoint_for_type(cap, eph, type, 3); case TK_TYPEPARAMREF: return viewpoint_for_type(cap, eph, type, 1); case TK_ARROW: { // Adapt the lower bounds. ast_t* lower = viewpoint_lower(type); ast_t* r_type = viewpoint_cap(cap, eph, lower); if(r_type != lower) ast_free_unattached(lower); return r_type; } default: {} } assert(0); return NULL; }
static matchtype_t could_subtype_arrow(ast_t* sub, ast_t* super) { if(ast_id(super) == TK_ARROW) { // If we have the same viewpoint, check the right side. AST_GET_CHILDREN(sub, sub_left, sub_right); AST_GET_CHILDREN(super, super_left, super_right); bool check = false; switch(ast_id(sub_left)) { case TK_THISTYPE: switch(ast_id(super_left)) { case TK_THISTYPE: case TK_BOXTYPE: check = true; break; default: {} } break; case TK_BOXTYPE: check = ast_id(super_left) == TK_BOXTYPE; break; default: check = is_eqtype(sub_left, super_left); break; } if(check) return could_subtype(sub_right, super_right); } // Check the lower bounds. ast_t* lower = viewpoint_lower(sub); matchtype_t ok = could_subtype(super, lower); if(lower != sub) ast_free_unattached(lower); return ok; }
// The subtype is a nominal, the super type is a typeparam. static bool is_nominal_sub_typeparam(ast_t* sub, ast_t* super) { // Must be a subtype of the lower bounds of the constraint. ast_t* def = (ast_t*)ast_data(super); ast_t* constraint = ast_childidx(def, 1); if(ast_id(constraint) == TK_NOMINAL) { ast_t* constraint_def = (ast_t*)ast_data(constraint); switch(ast_id(constraint_def)) { case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: { // Constraint must be modified with super ephemerality. AST_GET_CHILDREN(super, name, cap, eph); ast_t* r_constraint = set_cap_and_ephemeral(constraint, TK_NONE, ast_id(eph)); // Must be a subtype of the constraint. bool ok = is_subtype(sub, constraint); ast_free_unattached(r_constraint); if(!ok) return false; // Capability must be a subtype of the lower bounds of the typeparam. ast_t* lower = viewpoint_lower(super); ok = is_sub_cap_and_ephemeral(sub, lower); ast_free_unattached(lower); return ok; } default: {} } } return false; }
static matchtype_t is_arrow_match_x(ast_t* operand, ast_t* pattern, errorframe_t* errorf, bool report_reject, pass_opt_t* opt) { // upperbound(this->T1) match T2 // --- // (this->T1) match T2 // lowerbound(T1->T2) match T3 // --- // (T1->T2) match T3 ast_t* operand_view; AST_GET_CHILDREN(operand, left, right); if(ast_id(left) == TK_THISTYPE) operand_view = viewpoint_upper(operand); else operand_view = viewpoint_lower(operand); if(operand_view == NULL) { if(errorf != NULL) { // this->X always has an upper bound. pony_assert(ast_id(left) != TK_THISTYPE); ast_error_frame(errorf, pattern, "matching %s with %s could violate capabilities: " "the match type has no lower bounds", ast_print_type(operand), ast_print_type(pattern)); } return MATCHTYPE_DENY; } matchtype_t ok = is_x_match_x(operand_view, pattern, errorf, report_reject, opt); ast_free_unattached(operand_view); return ok; }
static bool is_typeparam_sub_arrow(ast_t* sub, ast_t* super, errorframe_t* errors) { // forall k' in k . A k' <: lowerbound(T1->T2 {A k |-> A k'}) // --- // A k <: T1->T2 ast_t* r_sub = viewpoint_reifytypeparam(sub, sub); ast_t* r_super = viewpoint_reifytypeparam(super, sub); if(r_sub != NULL) { bool ok = is_subtype(r_sub, r_super, errors); ast_free_unattached(r_sub); ast_free_unattached(r_super); return ok; } // If there is only a single instantiation, calculate the lower bounds. // // A k <: lowerbound(T1->T2) // --- // A k <: T1->T2 ast_t* super_lower = viewpoint_lower(super); if(super_lower == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the supertype has no lower bounds", ast_print_type(sub), ast_print_type(super)); } return false; } bool ok = is_subtype(sub, super_lower, errors); ast_free_unattached(super_lower); return ok; }
static bool is_nominal_sub_arrow(ast_t* sub, ast_t* super, errorframe_t* errors) { // N k <: lowerbound(T1->T2) // --- // N k <: T1->T2 ast_t* super_lower = viewpoint_lower(super); if(super_lower == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the supertype has no lower bounds", ast_print_type(sub), ast_print_type(super)); } return false; } bool ok = is_subtype(sub, super_lower, errors); ast_free_unattached(super_lower); return ok; }
ast_t* viewpoint_lower(ast_t* type) { // T = N | A // s = {k} // upper(s p'->T k p) = union[k' in s](T (k'->k) eph(s, p', p)) // eph(s, p', p) = { unalias(p) if p' = ^, exists k in s . k in {iso, trn} // { p otherwise assert(ast_id(type) == TK_ARROW); AST_GET_CHILDREN(type, left, right); ast_t* r_right = right; switch(ast_id(right)) { case TK_NOMINAL: case TK_TYPEPARAMREF: break; case TK_ARROW: // Arrow types are right associative. r_right = viewpoint_lower(right); if(r_right == NULL) return NULL; break; default: assert(0); return NULL; } token_id l_cap = TK_NONE; token_id l_eph = TK_NONE; switch(ast_id(left)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: l_cap = ast_id(left); break; case TK_THISTYPE: l_cap = TK_CAP_READ; break; case TK_NOMINAL: case TK_TYPEPARAMREF: { ast_t* left_cap = cap_fetch(left); ast_t* left_eph = ast_sibling(left_cap); l_cap = ast_id(left_cap); l_eph = ast_id(left_eph); break; } default: assert(0); return NULL; } ast_t* right_cap = cap_fetch(r_right); ast_t* right_eph = ast_sibling(right_cap); token_id r_cap = ast_id(right_cap); token_id r_eph = ast_id(right_eph); // No result: left side could be a tag. if(!cap_view_lower(l_cap, l_eph, &r_cap, &r_eph)) return NULL; ast_t* rr_right = set_cap_and_ephemeral(r_right, r_cap, r_eph); if(r_right != right) ast_free_unattached(r_right); return rr_right; }
static bool is_arrow_sub_arrow(ast_t* sub, ast_t* super, errorframe_t* errors) { // S = this | A {#read, #send, #share, #any} // K = N k | A {iso, trn, ref, val, box, tag} | K->K | (empty) // L = S | K // T = N k | A k | L->T // // forall K' in S . K->S->T1 {S |-> K'} <: T2 {S |-> K'} // --- // K->S->T1 <: T2 ast_t* r_sub; ast_t* r_super; if(viewpoint_reifypair(sub, super, &r_sub, &r_super)) { bool ok = is_subtype(r_sub, r_super, errors); ast_free_unattached(r_sub); ast_free_unattached(r_super); return ok; } // No elements need reification. // // upperbound(T1->T2) <: lowerbound(T3->T4) // --- // T1->T2 <: T3->T4 ast_t* sub_upper = viewpoint_upper(sub); ast_t* super_lower = viewpoint_lower(super); bool ok = true; if(sub_upper == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the subtype has no upper bounds", ast_print_type(sub), ast_print_type(super)); } ok = false; } if(super_lower == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the supertype has no lower bounds", ast_print_type(sub), ast_print_type(super)); } ok = false; } if(ok) ok = is_subtype(sub_upper, super_lower, errors); ast_free_unattached(sub_upper); ast_free_unattached(super_lower); return ok; }