// Reify the method with the type parameters from trait definition // and type arguments from trait reference. // Also handles modifying return types from behaviours, etc. // Returns the reified type, which must be freed later, or NULL on error. static ast_t* reify_provides_type(ast_t* method, ast_t* trait_ref, pass_opt_t* opt) { assert(method != NULL); assert(trait_ref != NULL); // Apply the type args (if any) from the trait reference to the type // parameters from the trait definition. ast_t* trait_def = (ast_t*)ast_data(trait_ref); assert(trait_def != NULL); ast_t* type_args = ast_childidx(trait_ref, 2); ast_t* type_params = ast_childidx(trait_def, 1); if(!reify_defaults(type_params, type_args, true, opt)) return NULL; ast_t* reified = reify(method, type_params, type_args, opt); if(reified == NULL) return NULL; AST_GET_CHILDREN(reified, cap, id, typeparams, params, result, can_error, body, doc); return reified; }
static bool check_type_params(pass_opt_t* opt, ast_t** astp) { ast_t* lhs = *astp; ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; ast_t* typeparams = ast_childidx(type, 1); assert(ast_id(type) == TK_FUNTYPE); if(ast_id(typeparams) == TK_NONE) return true; BUILD(typeargs, typeparams, NODE(TK_TYPEARGS)); if(!reify_defaults(typeparams, typeargs, true, opt)) { ast_free_unattached(typeargs); return false; } if(!check_constraints(lhs, typeparams, typeargs, true, opt)) { ast_free_unattached(typeargs); return false; } type = reify(type, typeparams, typeargs, opt); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); REPLACE(astp, NODE(ast_id(lhs), TREE(lhs) TREE(typeargs))); ast_settype(*astp, type); return true; }
bool expr_qualify(pass_opt_t* opt, ast_t** astp) { // Left is a postfix expression, right is a typeargs. ast_t* ast = *astp; AST_GET_CHILDREN(ast, left, right); ast_t* type = ast_type(left); assert(ast_id(right) == TK_TYPEARGS); if(is_typecheck_error(type)) return false; switch(ast_id(left)) { case TK_TYPEREF: { // Qualify the type. assert(ast_id(type) == TK_NOMINAL); // If the type isn't polymorphic or the type is already qualified, // sugar .apply(). ast_t* def = names_def(opt, type); ast_t* typeparams = ast_childidx(def, 1); if((ast_id(typeparams) == TK_NONE) || (ast_id(ast_childidx(type, 2)) != TK_NONE)) { if(!expr_nominal(opt, &type)) return false; break; } type = ast_dup(type); ast_t* typeargs = ast_childidx(type, 2); ast_replace(&typeargs, right); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: { // Qualify the function. assert(ast_id(type) == TK_FUNTYPE); ast_t* typeparams = ast_childidx(type, 1); if(!reify_defaults(typeparams, right, true, opt)) return false; if(!check_constraints(left, typeparams, right, true, opt)) return false; type = reify(type, typeparams, right, opt); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); ast_settype(ast, type); ast_setid(ast, ast_id(left)); ast_inheritflags(ast); return true; } default: {} } // Sugar .apply() ast_t* dot = ast_from(left, TK_DOT); ast_add(dot, ast_from_string(left, "apply")); ast_swap(left, dot); ast_add(dot, left); if(!expr_dot(opt, &dot)) return false; return expr_qualify(opt, astp); }
bool expr_nominal(pass_opt_t* opt, ast_t** astp) { // Resolve type aliases and typeparam references. if(!names_nominal(opt, *astp, astp, true)) return false; ast_t* ast = *astp; switch(ast_id(ast)) { case TK_TYPEPARAMREF: return flatten_typeparamref(ast) == AST_OK; case TK_NOMINAL: break; default: return true; } // If still nominal, check constraints. ast_t* def = (ast_t*)ast_data(ast); // Special case: don't check the constraint of a Pointer. This allows a // Pointer[Pointer[A]], which is normally not allowed, as a Pointer[A] is // not a subtype of Any. ast_t* id = ast_child(def); const char* name = ast_name(id); if(!strcmp(name, "Pointer")) return true; ast_t* typeparams = ast_childidx(def, 1); ast_t* typeargs = ast_childidx(ast, 2); if(!reify_defaults(typeparams, typeargs, true)) return false; if(!strcmp(name, "MaybePointer")) { // MaybePointer[A] must be bound to a struct. assert(ast_childcount(typeargs) == 1); ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); bool ok = false; switch(ast_id(typearg)) { case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(typearg); ok = ast_id(def) == TK_STRUCT; break; } case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(typearg); ok = def == typeparam; break; } default: {} } if(!ok) { ast_error(ast, "%s is not allowed: the type argument to MaybePointer must be a struct", ast_print_type(ast)); return false; } } return check_constraints(typeargs, typeparams, typeargs, true); }
bool expr_nominal(pass_opt_t* opt, ast_t** astp) { // Resolve type aliases and typeparam references. if(!names_nominal(opt, *astp, astp, true)) return false; ast_t* ast = *astp; switch(ast_id(ast)) { case TK_TYPEPARAMREF: return flatten_typeparamref(opt, ast) == AST_OK; case TK_NOMINAL: break; default: return true; } // If still nominal, check constraints. ast_t* def = (ast_t*)ast_data(ast); // Special case: don't check the constraint of a Pointer or an Array. These // builtin types have no contraint on their type parameter, and it is safe // to bind a struct as a type argument (which is not safe on any user defined // type, as that type might then be used for pattern matching). if(is_pointer(ast) || is_literal(ast, "Array")) return true; ast_t* typeparams = ast_childidx(def, 1); ast_t* typeargs = ast_childidx(ast, 2); if(!reify_defaults(typeparams, typeargs, true, opt)) return false; if(is_maybe(ast)) { // MaybePointer[A] must be bound to a struct. assert(ast_childcount(typeargs) == 1); ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); bool ok = false; switch(ast_id(typearg)) { case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(typearg); ok = ast_id(def) == TK_STRUCT; break; } case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(typearg); ok = def == typeparam; break; } default: {} } if(!ok) { ast_error(opt->check.errors, ast, "%s is not allowed: " "the type argument to MaybePointer must be a struct", ast_print_type(ast)); return false; } return true; } return check_constraints(typeargs, typeparams, typeargs, true, opt); }
// Process the method provided to the given entity. // Stage 2. static bool provided_methods(pass_opt_t* opt, ast_t* entity) { assert(entity != NULL); ast_t* provides = ast_childidx(entity, 3); ast_t* entity_members = ast_childidx(entity, 4); ast_t* last_member = ast_childlast(entity_members); bool r = true; // Run through our provides list for(ast_t* p = ast_child(provides); p != NULL; p = ast_sibling(p)) { ast_t* trait_def = (ast_t*)ast_data(p); assert(trait_def != NULL); ast_t* type_args = ast_childidx(p, 2); ast_t* type_params = ast_childidx(trait_def, 1); ast_t* members = ast_childidx(trait_def, 4); // Run through the methods of each provided type for(ast_t* m = ast_child(members); m != NULL; m = ast_sibling(m)) { // Check behaviour compatability if(ast_id(m) == TK_BE) { switch(ast_id(entity)) { case TK_PRIMITIVE: ast_error(entity, "primitives can't provide traits that have behaviours"); r = false; continue; case TK_STRUCT: ast_error(entity, "structs can't provide traits that have behaviours"); r = false; continue; case TK_CLASS: ast_error(entity, "classes can't provide traits that have behaviours"); r = false; continue; default: {} } } if(is_method(m)) { // We have a provided method // Reify the method with the type parameters from trait definition and // type arguments from trait reference if(!reify_defaults(type_params, type_args, true)) return false; ast_t* reified = reify(m, type_params, type_args); if(reified == NULL) // Reification error, already reported return false; if(!provided_method(opt, entity, reified, m, &last_member)) r = false; } } } return r; }