static bool is_recursive_interface(ast_t* sub, ast_t* super, ast_t* isub, ast_t* isuper) { if(isuper == NULL) { assert(isub == NULL); return false; } assert(ast_id(isub) == TK_NOMINAL); assert(ast_id(isuper) == TK_NOMINAL); return (ast_id(sub) == TK_NOMINAL) && (ast_id(super) == TK_NOMINAL) && is_eqtype(sub, isub) && is_eqtype(super, isuper); }
// Attach the appropriate body (if any) from the given method list to the given // entity method static void attach_body_from_list(ast_t* list, ast_t* entity_method) { assert(list != NULL); assert(entity_method != NULL); void* data = ast_data(entity_method); for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { void* p_data = ast_data(p); if(p_data != NULL && p_data != data && is_eqtype(entity_method, p)) { // p has a valid (and different) body if(data != NULL || p_data == BODY_AMBIGUOUS) { // Multiple possible bodies ast_setdata(entity_method, BODY_AMBIGUOUS); return; } // This is the first valid body, use it ast_t* old_body = ast_childidx(entity_method, 6); assert(ast_id(old_body) == TK_NONE); ast_replace(&old_body, ast_childidx(p, 6)); ast_setdata(entity_method, p_data); data = p_data; } } }
// 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 could_subtype_typearg(ast_t* sub, ast_t* super) { token_id tsub = ast_id(sub); token_id tsup = ast_id(super); if(tsub == TK_TYPEPARAMREF) return could_subtype(sub, super) == MATCHTYPE_ACCEPT; if(tsup == TK_TYPEPARAMREF) return could_subtype(super, sub) == MATCHTYPE_ACCEPT; if((tsub == TK_TUPLETYPE) && (tsup == TK_TUPLETYPE)) { ast_t* sub_child = ast_child(sub); ast_t* sup_child = ast_child(super); while((sub_child != NULL) && (sup_child != NULL)) { if(!could_subtype_typearg(sub_child, sup_child)) return false; sub_child = ast_sibling(sub_child); sup_child = ast_sibling(sup_child); } if((sub_child != NULL) || (sup_child != NULL)) return false; return true; } return is_eqtype(sub, super); }
// 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 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; }
static void add_types_to_trait(reachable_method_stack_t** s, reachable_types_t* r, reachable_type_t* t) { size_t i = HASHMAP_BEGIN; reachable_type_t* t2; ast_t* def = (ast_t*)ast_data(t->type); bool interface = ast_id(def) == TK_INTERFACE; while((t2 = reachable_types_next(r, &i)) != NULL) { if(ast_id(t2->type) == TK_TUPLETYPE) continue; ast_t* def2 = (ast_t*)ast_data(t2->type); switch(ast_id(def2)) { case TK_INTERFACE: { // Use the same typeid. if(interface && is_eqtype(t->type, t2->type, NULL)) t->type_id = t2->type_id; break; } case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: if(is_subtype(t2->type, t->type, NULL)) { reachable_type_cache_put(&t->subtypes, t2); reachable_type_cache_put(&t2->subtypes, t); add_methods_to_type(s, t, t2); } break; default: {} } } }
static void add_types_to_trait(reach_t* r, reach_type_t* t, pass_opt_t* opt) { size_t i = HASHMAP_BEGIN; reach_type_t* t2; ast_t* def = (ast_t*)ast_data(t->ast); bool interface = ast_id(def) == TK_INTERFACE; while((t2 = reach_types_next(&r->types, &i)) != NULL) { if(ast_id(t2->ast) != TK_NOMINAL) continue; ast_t* def2 = (ast_t*)ast_data(t2->ast); switch(ast_id(def2)) { case TK_INTERFACE: { // Use the same typeid. if(interface && is_eqtype(t->ast, t2->ast, NULL, opt)) t->type_id = t2->type_id; break; } case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: if(is_subtype(t2->ast, t->ast, NULL, opt)) { reach_type_cache_put(&t->subtypes, t2); reach_type_cache_put(&t2->subtypes, t); add_methods_to_type(r, t, t2, opt); } break; default: {} } } }
static bool is_eq_typeargs(ast_t* a, ast_t* b) { assert(ast_id(a) == TK_NOMINAL); assert(ast_id(b) == TK_NOMINAL); // Check typeargs are the same. ast_t* a_arg = ast_child(ast_childidx(a, 2)); ast_t* b_arg = ast_child(ast_childidx(b, 2)); while((a_arg != NULL) && (b_arg != NULL)) { if(!is_eqtype(a_arg, b_arg)) return false; a_arg = ast_sibling(a_arg); b_arg = ast_sibling(b_arg); } // Make sure we had the same number of typeargs. return (a_arg == NULL) && (b_arg == NULL); }
static bool is_eq_typeargs(ast_t* a, ast_t* b, errorframe_t* errors) { assert(ast_id(a) == TK_NOMINAL); assert(ast_id(b) == TK_NOMINAL); // Check typeargs are the same. ast_t* a_arg = ast_child(ast_childidx(a, 2)); ast_t* b_arg = ast_child(ast_childidx(b, 2)); bool ret = true; while((a_arg != NULL) && (b_arg != NULL)) { if(!is_eqtype(a_arg, b_arg, errors)) ret = false; a_arg = ast_sibling(a_arg); b_arg = ast_sibling(b_arg); } if(!ret && errors != NULL) { ast_error_frame(errors, a, "%s has different type arguments than %s", ast_print_type(a), ast_print_type(b)); } // Make sure we had the same number of typeargs. if((a_arg != NULL) || (b_arg != NULL)) { if(errors != NULL) { ast_error_frame(errors, a, "%s has a different number of type arguments than %s", ast_print_type(a), ast_print_type(b)); } ret = false; } return ret; }
static ast_result_t declared_ffi(pass_opt_t* opt, ast_t* call, ast_t* decl) { assert(call != NULL); assert(decl != NULL); assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(call, call_name, call_ret_typeargs, args, named_args, call_error); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); // Check args vs params ast_t* param = ast_child(params); ast_t* arg = ast_child(args); while((arg != NULL) && (param != NULL) && ast_id(param) != TK_ELLIPSIS) { ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return AST_ERROR; ast_t* a_type = ast_type(arg); errorframe_t info = NULL; if((a_type != NULL) && !void_star_param(p_type, a_type) && !is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); ast_error_frame(&frame, param, "parameter type: %s", ast_print_type(p_type)); ast_error_frame(&frame, arg, "argument type: %s", ast_print_type(a_type)); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } arg = ast_sibling(arg); param = ast_sibling(param); } if(arg != NULL && param == NULL) { ast_error(opt->check.errors, arg, "too many arguments"); return AST_ERROR; } if(param != NULL && ast_id(param) != TK_ELLIPSIS) { ast_error(opt->check.errors, named_args, "too few arguments"); return AST_ERROR; } for(; arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return AST_ERROR; } } // Check return types ast_t* call_ret_type = ast_child(call_ret_typeargs); ast_t* decl_ret_type = ast_child(decl_ret_typeargs); errorframe_t info = NULL; if((call_ret_type != NULL) && !is_eqtype(call_ret_type, decl_ret_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, call_ret_type, "call return type does not match declaration"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } // Check partiality if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE)) { ast_error(opt->check.errors, call_error, "call is partial but the declaration is not"); return AST_ERROR; } if((ast_id(decl_error) == TK_QUESTION) || (ast_id(call_error) == TK_QUESTION)) { ast_seterror(call); } // Store the declaration so that codegen can generate a non-variadic // signature for the FFI call. ast_setdata(call, decl); ast_settype(call, decl_ret_type); ast_inheritflags(call); return AST_OK; }