// 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; }
// Setup a method_t structure for each method in the given type static void setup_local_methods(ast_t* ast) { assert(ast != NULL); ast_t* members = ast_childidx(ast, 4); assert(members != NULL); for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { if(is_method(p)) attach_method_t(p, ast, true); } }
// Setup a method_t structure for each method in the given type. static void setup_local_methods(ast_t* ast) { assert(ast != NULL); ast_t* members = ast_childidx(ast, 4); assert(members != NULL); for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { if(is_method(p)) { method_t* info = attach_method_t(p); info->local_define = true; if(ast_id(ast_childidx(p, 6)) != TK_NONE) info->body_donor = ast; } } }
// Add a new method to the given entity, based on the specified method from // the specified type. // The trait_ref is the entry in the provides / delegates list that causes this // method inclusion. Needed for error reporting. // The basis_method is the reified method in the trait to add. // The adjective parameter is used for error reporting and should be "provided" // or similar. // Return the newly added method or NULL on error. static ast_t* add_method(ast_t* entity, ast_t* trait_ref, ast_t* basis_method, const char* adjective, pass_opt_t* opt) { assert(entity != NULL); assert(trait_ref != NULL); assert(basis_method != NULL); assert(adjective != NULL); const char* name = ast_name(ast_childidx(basis_method, 1)); // Check behaviour compatability. if(ast_id(basis_method) == TK_BE) { switch(ast_id(entity)) { case TK_PRIMITIVE: ast_error(opt->check.errors, trait_ref, "cannot add a behaviour (%s) to a primitive", name); return NULL; case TK_STRUCT: ast_error(opt->check.errors, trait_ref, "cannot add a behaviour (%s) to a struct", name); return NULL; case TK_CLASS: ast_error(opt->check.errors, trait_ref, "cannot add a behaviour (%s) to a class", name); return NULL; default: break; } } // Check for existing method of the same name. ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL) { assert(is_field(existing)); // Should already have checked for methods. ast_error(opt->check.errors, trait_ref, "%s method '%s' clashes with field", adjective, name); ast_error_continue(opt->check.errors, basis_method, "method is defined here"); return NULL; } // Check for clash with existing method. ast_t* case_clash = ast_get_case(entity, name, NULL); if(case_clash != NULL) { const char* clash_name = ""; switch(ast_id(case_clash)) { case TK_FUN: case TK_BE: case TK_NEW: clash_name = ast_name(ast_childidx(case_clash, 1)); break; case TK_LET: case TK_VAR: case TK_EMBED: clash_name = ast_name(ast_child(case_clash)); break; default: assert(0); break; } ast_error(opt->check.errors, trait_ref, "%s method '%s' differs only in case from '%s'", adjective, name, clash_name); ast_error_continue(opt->check.errors, basis_method, "clashing method is defined here"); return NULL; } AST_GET_CHILDREN(basis_method, cap, id, typeparams, params, result, can_error, body, doc); // Ignore docstring. if(ast_id(doc) == TK_STRING) { ast_set_name(doc, ""); ast_setid(doc, TK_NONE); } ast_t* local = ast_append(ast_childidx(entity, 4), basis_method); ast_set(entity, name, local, SYM_DEFINED, false); ast_t* body_donor = (ast_t*)ast_data(basis_method); method_t* info = attach_method_t(local); info->trait_ref = trait_ref; if(ast_id(body) != TK_NONE) info->body_donor = body_donor; return local; }