bool is_eqtype(ast_t* a, ast_t* b) { if((ast_id(a) == TK_NOMINAL) && (ast_id(b) == TK_NOMINAL)) return is_nominal_eq_nominal(a, b); return is_subtype(a, b) && is_subtype(b, a); }
static bool is_x_sub_union(ast_t* sub, ast_t* super, errorframe_t* errors) { // TODO: a tuple may be a subtype of a union of tuples without being a // subtype of any one element of the union. // T1 <: T2 or T1 <: T3 // --- // T1 <: (T2 | T3) for(ast_t* child = ast_child(super); child != NULL; child = ast_sibling(child)) { if(is_subtype(sub, child, NULL)) return true; } if(errors != NULL) { for(ast_t* child = ast_child(super); child != NULL; child = ast_sibling(child)) { is_subtype(sub, child, errors); } ast_error_frame(errors, sub, "%s is not a subtype of any element of %s", ast_print_type(sub), ast_print_type(super)); } return false; }
// 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; }
static matchtype_t is_nominal_match_entity(ast_t* operand, ast_t* pattern) { AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph); AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph); // We say the pattern provides the operand if it is a subtype after changing // it to have the same refcap as the operand. token_id tcap = ast_id(p_cap); token_id teph = ast_id(p_eph); ast_t* r_operand = set_cap_and_ephemeral(operand, tcap, teph); bool provides = is_subtype(pattern, r_operand, false); ast_free_unattached(r_operand); // If the pattern doesn't provide the operand, reject the match. if(!provides) return MATCHTYPE_REJECT; // If the operand does provide the pattern, but the operand refcap can't // match the pattern refcap, deny the match. if(!is_cap_match_cap(ast_id(o_cap), ast_id(o_eph), tcap, teph)) return MATCHTYPE_DENY; // Otherwise, accept the match. return MATCHTYPE_ACCEPT; }
static void add_traits_to_type(reach_t* r, reach_type_t* t, pass_opt_t* opt) { size_t i = HASHMAP_BEGIN; reach_type_t* t2; while((t2 = reach_types_next(&r->types, &i)) != NULL) { if(ast_id(t2->ast) != TK_NOMINAL) continue; ast_t* def = (ast_t*)ast_data(t2->ast); switch(ast_id(def)) { case TK_INTERFACE: case TK_TRAIT: if(is_subtype(t->ast, t2->ast, NULL, opt)) { reach_type_cache_put(&t->subtypes, t2); reach_type_cache_put(&t2->subtypes, t); add_methods_to_type(r, t2, t, opt); } break; default: {} } } }
// Check that the given delegate target is in the entity's provides list and // is a legal type for the field static bool check_delegate(ast_t* entity, ast_t* field_type, ast_t* delegated) { assert(entity != NULL); assert(field_type != NULL); assert(delegated != NULL); if(!is_subtype(field_type, delegated, true)) { ast_error(delegated, "field not a subtype of delegate"); ast_error(field_type, "field type: %s", ast_print_type(field_type)); ast_error(delegated, "delegate type: %s", ast_print_type(delegated)); return false; } ast_t* provides = ast_childidx(entity, 3); void* del_def = ast_data(delegated); for(ast_t* p = ast_child(provides); p != NULL; p = ast_sibling(p)) { if(ast_data(p) == del_def) return true; } assert(ast_id(delegated) == TK_NOMINAL); ast_error(delegated, "cannot delegate to %s, containing entity does not provide it", ast_name(ast_childidx(delegated, 1))); return false; }
// Check that the given method is compatible with the existing definition. // Return the method that ends up being in the entity or NULL on error. // Stages 2A, 2B and 2C. static ast_t* add_method(ast_t* entity, ast_t* existing_method, ast_t* new_method, ast_t** last_method) { assert(entity != NULL); assert(new_method != NULL); assert(last_method != NULL); const char* name = ast_name(ast_childidx(new_method, 1)); const char* entity_name = ast_name(ast_child(entity)); if(existing_method == NULL) { // This method is new to the entity. // Stage 2C. ast_t* case_clash = ast_get_case(entity, name, NULL); if(case_clash != NULL) { ast_error(case_clash, "in %s method name %s differs only in case", entity_name, name); ast_error(new_method, "clashing method is here"); return NULL; } attach_method_t(new_method, NULL, false); ast_list_append(ast_childidx(entity, 4), last_method, new_method); ast_set(entity, name, new_method, SYM_DEFINED); return *last_method; } method_t* info = (method_t*)ast_data(existing_method); assert(info != NULL); if(info->local_def) { // Existing method is a local definition, new method must be a subtype // Stage 2A if(is_subtype(existing_method, new_method, true)) return existing_method; ast_error(existing_method, "local method %s is not compatible with provided version", name); ast_error(new_method, "clashing method is here"); return NULL; } // Both method versions came from the provides list, their signatures must // match exactly // Stage 2B if(compare_signatures(existing_method, new_method)) return existing_method; ast_error(entity, "clashing definitions of method %s provided, local " "disambiguation required", name); ast_error(existing_method, "provided here"); ast_error(new_method, "and here"); return NULL; }
bool expr_field(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); if(ast_id(init) != TK_NONE) { // Initialiser type must match declared type. if(!coerce_literals(&init, type, opt)) return false; ast_t* init_type = ast_type(init); if(is_typecheck_error(init_type)) return false; init_type = alias(init_type); if(!is_subtype(init_type, type)) { ast_error(init, "field/param initialiser is not a subtype of the field/param type"); ast_error(type, "field/param type: %s", ast_print_type(type)); ast_error(init, "initialiser type: %s", ast_print_type(init_type)); ast_free_unattached(init_type); return false; } ast_free_unattached(init_type); } ast_settype(ast, type); return true; }
static void add_traits_to_type(reachable_method_stack_t** s, reachable_types_t* r, reachable_type_t* t) { size_t i = HASHMAP_BEGIN; reachable_type_t* t2; while((t2 = reachable_types_next(r, &i)) != NULL) { if(ast_id(t2->type) == TK_TUPLETYPE) continue; ast_t* def = (ast_t*)ast_data(t2->type); switch(ast_id(def)) { case TK_INTERFACE: case TK_TRAIT: if(is_subtype(t->type, t2->type, NULL)) { reachable_type_cache_put(&t->subtypes, t2); reachable_type_cache_put(&t2->subtypes, t); add_methods_to_type(s, t2, t); } break; default: {} } } }
//Returns "true" if a is a subtype of b int is_subtype(polytype a, polytype b) { if (polytype_trivial_eq(b, Top)) { return 1; //Everything is a subtype of "top" } //Is A possibly a subtype of b? if (Type_graph_possiblesubtype(UniverseGraph, a, b)) { //Is A the same as B? if (polytype_trivial_eq(a, b)) { return 1; } //Otherwise, must not be, so recurse on b's children //TODO: Eliminate instantiation here! polytype_dynarray b_subtypes = typeslot_dynarray_instantiate(polytype_get_subtypes(b)); int i; for (i=0; i < b_subtypes.size; i++) { //If a is a subtype of any subtype of b if (is_subtype(a, b_subtypes.begin[i])) { //Then it's a subtype of b return 1; } } } //In all other cases, it can't be a subtype of b return 0; }
// The subtype is an arrow, the supertype is an arrow. static bool is_arrow_sub_arrow(ast_t* sub, ast_t* super) { // If the supertype is also a viewpoint and the viewpoint is the same, then // we are a subtype if our adapted type is a subtype of the supertype's // adapted type. AST_GET_CHILDREN(sub, sub_left, sub_right); AST_GET_CHILDREN(super, super_left, super_right); switch(ast_id(sub_left)) { case TK_THISTYPE: switch(ast_id(super_left)) { case TK_THISTYPE: case TK_BOXTYPE: break; default: return false; } break; case TK_BOXTYPE: if(ast_id(super_left) != TK_BOXTYPE) return false; break; default: if(!is_eqtype(sub_left, super_left)) return false; break; } return is_subtype(sub_right, super_right); }
static bool is_union_sub_x(ast_t* sub, ast_t* super, errorframe_t* errors) { // T1 <: T3 // T2 <: T3 // --- // (T1 | T2) <: T3 for(ast_t* child = ast_child(sub); child != NULL; child = ast_sibling(child)) { if(!is_subtype(child, super, errors)) { if(errors != NULL) { ast_error_frame(errors, child, "not every element of %s is a subtype of %s", ast_print_type(sub), ast_print_type(super)); } return false; } } return true; }
static bool is_x_sub_isect(ast_t* sub, ast_t* super, errorframe_t* errors) { // T1 <: T2 // T1 <: T3 // --- // T1 <: (T2 & T3) for(ast_t* child = ast_child(super); child != NULL; child = ast_sibling(child)) { if(!is_subtype(sub, child, errors)) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of every element of %s", ast_print_type(sub), ast_print_type(super)); } return false; } } return true; }
static bool is_typeparam_sub_x(ast_t* sub, ast_t* super, errorframe_t* errors) { if(is_typeparam_base_sub_x(sub, super, false)) return true; // upperbound(A k) <: T // --- // A k <: T ast_t* sub_upper = typeparam_upper(sub); if(sub_upper == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the subtype has no constraint", ast_print_type(sub), ast_print_type(super)); } return false; } bool ok = is_subtype(sub_upper, super, errors); ast_free_unattached(sub_upper); return ok; }
static matchtype_t is_entity_match_trait(ast_t* operand, ast_t* pattern, pass_opt_t* opt) { AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph); AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph); token_id tcap = ast_id(p_cap); token_id teph = ast_id(p_eph); ast_t* r_operand = set_cap_and_ephemeral(operand, tcap, teph); bool provides = is_subtype(r_operand, pattern, NULL, opt); ast_free_unattached(r_operand); // If the operand doesn't provide the pattern (trait or interface), reject // the match. if(!provides) return MATCHTYPE_REJECT; // If the operand does provide the pattern, but the operand refcap can't // match the pattern refcap, deny the match. if(!is_cap_match_cap(ast_id(o_cap), ast_id(o_eph), tcap, teph)) return MATCHTYPE_DENY; // Otherwise, accept the match. return MATCHTYPE_ACCEPT; }
bool expr_param(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); ast_settype(ast, type); bool ok = true; if(ast_id(init) != TK_NONE) { // Initialiser type must match declared type. if(!coerce_literals(&init, type, opt)) return false; ast_t* init_type = ast_type(init); if(is_typecheck_error(init_type)) return false; init_type = alias(init_type); errorframe_t err = NULL; if(!is_subtype(init_type, type, &err, opt)) { errorframe_t err2 = NULL; ast_error_frame(&err2, init, "default argument is not a subtype of the parameter type"); errorframe_append(&err2, &err); errorframe_report(&err2, opt->check.errors); ok = false; } ast_free_unattached(init_type); } return ok; }
// Find the most general method from the given list (if any) // Return NULL if no most general found static ast_t* most_general_method(ast_t* list, ast_t* entity, const char* name) { assert(list != NULL); assert(entity != NULL); assert(name != NULL); for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { bool is_p_best = true; for(ast_t* q = ast_child(list); q != NULL; q = ast_sibling(q)) { if(ast_id(p) != ast_id(q)) { ast_error(entity, "Clashing types for method %s provided by traits", name); return NULL; } if(!is_subtype(p, q)) { // p is less general than q is_p_best = false; break; } } if(is_p_best) return p; } ast_error(entity, "Clashing types for method %s provided by traits", name); 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 token_id partial_application_cap(pass_opt_t* opt, ast_t* ftype, ast_t* receiver, ast_t* positional) { // Check if the apply method in the generated object literal can accept a box // receiver. If not, it must be a ref receiver. It can accept a box receiver // if box->receiver <: lhs->receiver and box->arg <: lhs->param. AST_GET_CHILDREN(ftype, cap, typeparams, params, result); ast_t* type = ast_type(receiver); ast_t* view_type = viewpoint_type(ast_from(type, TK_BOX), type); ast_t* need_type = set_cap_and_ephemeral(type, ast_id(cap), TK_NONE); bool ok = is_subtype(view_type, need_type, NULL, opt); ast_free_unattached(view_type); ast_free_unattached(need_type); if(!ok) return TK_REF; ast_t* param = ast_child(params); ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) != TK_NONE) { type = ast_type(arg); view_type = viewpoint_type(ast_from(type, TK_BOX), type); need_type = ast_childidx(param, 1); ok = is_subtype(view_type, need_type, NULL, opt); ast_free_unattached(view_type); ast_free_unattached(need_type); if(!ok) return TK_REF; } arg = ast_sibling(arg); param = ast_sibling(param); } return TK_BOX; }
static bool check_return_type(ast_t* ast) { AST_GET_CHILDREN(ast, cap, id, typeparams, params, type, can_error, body); ast_t* body_type = ast_type(body); if(is_typecheck_error(body_type)) return false; // The last statement is an error, and we've already checked any return // expressions in the method. if(is_control_type(body_type)) return true; // If it's a compiler intrinsic, ignore it. if(ast_id(body_type) == TK_COMPILE_INTRINSIC) return true; // The body type must match the return type, without subsumption, or an alias // of the body type must be a subtype of the return type. ast_t* a_type = alias(type); ast_t* a_body_type = alias(body_type); bool ok = true; errorframe_t info = NULL; if(!is_subtype(body_type, type, &info) || !is_subtype(a_body_type, a_type, &info)) { errorframe_t frame = NULL; ast_t* last = ast_childlast(body); ast_error_frame(&frame, last, "function body isn't the result type"); ast_error_frame(&frame, type, "function return type: %s", ast_print_type(type)); ast_error_frame(&frame, body_type, "function body type: %s", ast_print_type(body_type)); errorframe_append(&frame, &info); errorframe_report(&frame); ok = false; } ast_free_unattached(a_type); ast_free_unattached(a_body_type); return ok; }
bool expr_field(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); // An embedded field must have a known, non-actor type. if(ast_id(ast) == TK_EMBED) { if(!is_known(type) || is_actor(type)) { ast_error(ast, "embedded fields must always be primitives or classes"); return false; } } if(ast_id(init) != TK_NONE) { // Initialiser type must match declared type. if(!coerce_literals(&init, type, opt)) return false; ast_t* init_type = ast_type(init); if(is_typecheck_error(init_type)) return false; init_type = alias(init_type); if(!is_subtype(init_type, type)) { ast_error(init, "field/param initialiser is not a subtype of the field/param type"); ast_error(type, "field/param type: %s", ast_print_type(type)); ast_error(init, "initialiser type: %s", ast_print_type(init_type)); ast_free_unattached(init_type); return false; } // If it's an embedded field, check for a constructor result. if(ast_id(ast) == TK_EMBED) { if((ast_id(init) != TK_CALL) || (ast_id(ast_childidx(init, 2)) != TK_NEWREF)) { ast_error(ast, "an embedded field must be initialised using a constructor"); return false; } } ast_free_unattached(init_type); } ast_settype(ast, type); return true; }
static matchtype_t could_subtype_trait_nominal(ast_t* sub, ast_t* super) { ast_t* def = (ast_t*)ast_data(super); switch(ast_id(def)) { case TK_INTERFACE: case TK_TRAIT: return could_subtype_trait_trait(sub, super); case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: { // Super must provide sub, ignoring cap/eph, else reject. AST_GET_CHILDREN(super, sup_pkg, sup_id, sup_typeargs, sup_cap, sup_eph); ast_t* r_type = set_cap_and_ephemeral(sub, ast_id(sup_cap), ast_id(sup_eph)); if(!is_subtype(super, r_type)) { ast_free_unattached(r_type); return MATCHTYPE_REJECT; } // Sub cap/eph must be a subtype of super cap/eph, else deny. if(!is_subtype(sub, r_type)) { ast_free_unattached(r_type); return MATCHTYPE_DENY; } // Otherwise, accept. return MATCHTYPE_ACCEPT; } default: {} } assert(0); return MATCHTYPE_DENY; }
static ast_t* type_typeexpr(token_id t, ast_t* l_type, ast_t* r_type) { bool is_union = t == TK_UNIONTYPE; if(l_type == NULL) return r_type; if(r_type == NULL) return l_type; if(is_subtype(l_type, r_type)) { if(is_union) return r_type; else return l_type; } if(is_subtype(r_type, l_type)) { if(is_union) return l_type; else return r_type; } ast_t* type = ast_from(l_type, t); append_to_typeexpr(type, l_type, is_union); append_to_typeexpr(type, r_type, is_union); // If there's only one element, remove the type expression node. ast_t* child = ast_child(type); if(ast_sibling(child) == NULL) { child = ast_dup(child); ast_free_unattached(type); type = child; } return type; }
static matchtype_t could_subtype_trait_trait(ast_t* sub, ast_t* super) { // Sub cap/eph must be a subtype of super cap/eph, else deny. // Otherwise, accept. AST_GET_CHILDREN(super, sup_pkg, sup_id, sup_typeargs, sup_cap, sup_eph); ast_t* r_type = set_cap_and_ephemeral(sub, ast_id(sup_cap), ast_id(sup_eph)); bool ok = is_subtype(sub, r_type); ast_free_unattached(r_type); return ok ? MATCHTYPE_ACCEPT : MATCHTYPE_DENY; }
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; }
bool is_signed(pass_opt_t* opt, ast_t* type) { if(type == NULL) return false; ast_t* builtin = type_builtin(opt, type, "Signed"); if(builtin == NULL) return false; bool ok = is_subtype(type, builtin); ast_free_unattached(builtin); return ok; }
// The subtype is a typeparam, the supertype could be anything. static bool is_typeparam_subtype(ast_t* sub, ast_t* super) { switch(ast_id(super)) { case TK_TYPEPARAMREF: if(is_typeparam_sub_typeparam(sub, super)) return true; break; case TK_UNIONTYPE: if(is_subtype_union(sub, super)) return true; break; case TK_ISECTTYPE: if(is_subtype_isect(sub, super)) return true; break; case TK_ARROW: if(is_subtype_arrow(sub, super)) return true; break; default: {} } // We can be a subtype if our upper bounds, ie our constraint, is a subtype. ast_t* sub_def = (ast_t*)ast_data(sub); ast_t* constraint = ast_childidx(sub_def, 1); if(ast_id(constraint) == TK_TYPEPARAMREF) { ast_t* constraint_def = (ast_t*)ast_data(constraint); if(constraint_def == sub_def) return false; } // Constraint must be modified with sub ephemerality. AST_GET_CHILDREN(sub, name, cap, eph); ast_t* r_constraint = set_cap_and_ephemeral(constraint, TK_NONE, ast_id(eph)); bool ok = is_subtype(r_constraint, super); ast_free_unattached(r_constraint); return ok; }
// The subtype is a union, the supertype could be anything. static bool is_union_subtype(ast_t* sub, ast_t* super) { // All elements of the union must be a subtype of super. ast_t* child = ast_child(sub); while(child != NULL) { if(!is_subtype(child, super)) return false; child = ast_sibling(child); } return true; }
// The subtype is either a nominal or a tuple, the super type is an isect. static bool is_subtype_isect(ast_t* sub, ast_t* super) { // Must be a subtype of all elements of the intersection. ast_t* child = ast_child(super); while(child != NULL) { if(!is_subtype(sub, child)) return false; child = ast_sibling(child); } return true; }
// The subtype is either a nominal or a tuple, the super type is a union. static bool is_subtype_union(ast_t* sub, ast_t* super) { // Must be a subtype of one element of the union. ast_t* child = ast_child(super); while(child != NULL) { if(is_subtype(sub, child)) return true; child = ast_sibling(child); } return false; }