// Sort out symbol table for copied method body static ast_result_t rescope(ast_t** astp, pass_opt_t* options) { (void)options; ast_t* ast = *astp; if(ast_has_scope(ast)) ast_clear_local(ast); switch(ast_id(ast)) { case TK_FVAR: case TK_FLET: case TK_PARAM: case TK_TYPEPARAM: assert(ast_child(ast) != NULL); ast_set(ast, ast_name(ast_child(ast)), ast, SYM_DEFINED); break; case TK_LET: case TK_VAR: { ast_t* scope = ast_parent(ast); ast_t* id = ast_child(ast); ast_set(scope, ast_name(id), id, SYM_DEFINED); break; } default: {} } return AST_OK; }
// Sort out symbol table for copied method body. static ast_result_t rescope(ast_t** astp, pass_opt_t* options) { (void)options; ast_t* ast = *astp; if(ast_has_scope(ast)) ast_clear_local(ast); switch(ast_id(ast)) { case TK_FVAR: case TK_FLET: case TK_EMBED: case TK_PARAM: case TK_TYPEPARAM: { assert(ast_child(ast) != NULL); ast_set(ast, ast_name(ast_child(ast)), ast, SYM_DEFINED, true); break; } case TK_LET: case TK_VAR: { ast_t* scope = ast_parent(ast); ast_t* id = ast_child(ast); ast_set(scope, ast_name(id), id, SYM_UNDEFINED, true); break; } case TK_MATCH_CAPTURE: { ast_t* scope = ast_parent(ast); ast_t* id = ast_child(ast); ast_set(scope, ast_name(id), id, SYM_DEFINED, true); break; } case TK_TYPEPARAMREF: { assert(ast_child(ast) != NULL); ast_t* def = ast_get(ast, ast_name(ast_child(ast)), NULL); ast_setdata(ast, def); break; } default: {} } return AST_OK; }
// Create a package AST, set up its state and add it to the given program ast_t* create_package(ast_t* program, const char* name, const char* qualified_name, pass_opt_t* opt) { ast_t* package = ast_blank(TK_PACKAGE); uint32_t pkg_id = program_assign_pkg_id(program); package_t* pkg = POOL_ALLOC(package_t); pkg->path = name; pkg->qualified_name = qualified_name; pkg->id = id_to_string(NULL, pkg_id); const char* p = strrchr(pkg->path, PATH_SLASH); if(p == NULL) p = pkg->path; else p = p + 1; pkg->filename = stringtab(p); if(pkg_id > 1) pkg->symbol = create_package_symbol(program, pkg->filename); else pkg->symbol = NULL; pkg->ast = package; package_set_init(&pkg->dependencies, 1); pkg->group = NULL; pkg->group_index = -1; pkg->next_hygienic_id = 0; pkg->low_index = -1; ast_setdata(package, pkg); ast_scope(package); ast_append(program, package); ast_set(program, pkg->path, package, SYM_NONE, false); ast_set(program, pkg->id, package, SYM_NONE, false); strlist_t* safe = opt->safe_packages; if((safe != NULL) && (strlist_find(safe, pkg->path) == NULL)) pkg->allow_ffi = false; else pkg->allow_ffi = true; pkg->on_stack = false; return package; }
// Process the given list of provided methods (all of the same name) for the // given entity static bool process_method_name(ast_t* list, ast_t* entity) { assert(list != NULL); assert(entity != NULL); const char* name = ast_name(list); assert(name != NULL); ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL) { // Method is explicitly defined in entity if(!methods_compatible(list, existing, name, entity)) return false; } else { // Method is not defined in entity existing = most_general_method(list, entity, name); if(existing == NULL) return false; // Add the most general method from the list to the entity existing = ast_dup(existing); ast_append(ast_childidx(entity, 4), existing); ast_set(entity, name, existing, SYM_NONE); } // Current body (if any) is not provided by this entity, get one from the // trait methods if(ast_data(existing) != (void*)entity) attach_body_from_list(list, existing); return true; }
// 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; }
// Create a package AST, set up its state and add it to the given program static ast_t* create_package(ast_t* program, const char* name) { ast_t* package = ast_blank(TK_PACKAGE); uint32_t pkg_id = program_assign_pkg_id(program); package_t* pkg = POOL_ALLOC(package_t); pkg->path = name; pkg->id = id_to_string(NULL, pkg_id); const char* p = strrchr(pkg->path, PATH_SLASH); if(p == NULL) p = pkg->path; else p = p + 1; pkg->filename = stringtab(p); if(pkg_id > 1) pkg->symbol = create_package_symbol(program, pkg->filename); else pkg->symbol = NULL; pkg->next_hygienic_id = 0; ast_setdata(package, pkg); ast_scope(package); ast_append(program, package); ast_set(program, pkg->path, package, SYM_NONE); ast_set(program, pkg->id, package, SYM_NONE); if((safe != NULL) && (strlist_find(safe, pkg->path) == NULL)) pkg->allow_ffi = false; else pkg->allow_ffi = true; return package; }
/** * Insert a name->AST mapping into the specified scope. */ static bool set_scope(typecheck_t* t, ast_t* scope, ast_t* name, ast_t* value) { assert(ast_id(name) == TK_ID); const char* s = ast_name(name); sym_status_t status = SYM_NONE; switch(ast_id(value)) { case TK_ID: { if((t != NULL) && (t->frame->pattern != NULL)) status = SYM_DEFINED; else status = SYM_UNDEFINED; break; } case TK_FFIDECL: case TK_TYPE: case TK_INTERFACE: case TK_TRAIT: case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: case TK_TYPEPARAM: case TK_PACKAGE: case TK_NEW: case TK_BE: case TK_FUN: break; case TK_FVAR: case TK_FLET: case TK_EMBED: status = SYM_DEFINED; break; case TK_PARAM: status = SYM_DEFINED; break; default: assert(0); return false; } if(!ast_set(scope, s, value, status)) { ast_t* prev = ast_get(scope, s, NULL); ast_t* prev_nocase = ast_get_case(scope, s, NULL); ast_error(name, "can't reuse name '%s'", s); ast_error(prev_nocase, "previous use of '%s'%s", s, (prev == NULL) ? " differs only by case" : ""); return false; } return true; }
// Add eq() and ne() functions to the given entity. static bool add_comparable(ast_t* ast, pass_opt_t* options) { assert(ast != NULL); AST_GET_CHILDREN(ast, id, typeparams, defcap, traits, members); ast_t* typeargs = ast_from(typeparams, TK_NONE); bool r = true; for(ast_t* p = ast_child(typeparams); p != NULL; p = ast_sibling(p)) { ast_t* p_id = ast_child(p); BUILD_NO_DEBUG(type, p_id, NODE(TK_NOMINAL, NONE TREE(p_id) NONE NONE NONE)); ast_append(typeargs, type); ast_setid(typeargs, TK_TYPEARGS); } if(!has_member(members, "eq")) { BUILD_NO_DEBUG(eq, members, NODE(TK_FUN, AST_SCOPE NODE(TK_BOX) ID("eq") NONE NODE(TK_PARAMS, NODE(TK_PARAM, ID("that") NODE(TK_NOMINAL, NONE TREE(id) TREE(typeargs) NONE NONE) NONE)) NODE(TK_NOMINAL, NONE ID("Bool") NONE NONE NONE) NONE NODE(TK_SEQ, NODE(TK_IS, NODE(TK_THIS) NODE(TK_REFERENCE, ID("that")))) NONE NONE)); // Need to set function data field to point to originating type, ie ast. // This won't be done when we catch up the passes since we've already // processed that type. ast_setdata(eq, ast); ast_append(members, eq); ast_set(ast, stringtab("eq"), eq, SYM_DEFINED); if(!ast_passes_subtree(&eq, options, PASS_TRAITS)) r = false; } if(!has_member(members, "ne")) { BUILD_NO_DEBUG(ne, members, NODE(TK_FUN, AST_SCOPE NODE(TK_BOX) ID("ne") NONE NODE(TK_PARAMS, NODE(TK_PARAM, ID("that") NODE(TK_NOMINAL, NONE TREE(id) TREE(typeargs) NONE NONE) NONE)) NODE(TK_NOMINAL, NONE ID("Bool") NONE NONE NONE) NONE NODE(TK_SEQ, NODE(TK_ISNT, NODE(TK_THIS) NODE(TK_REFERENCE, ID("that")))) NONE NONE)); // Need to set function data field to point to originating type, ie ast. // This won't be done when we catch up the passes since we've already // processed that type. ast_setdata(ne, ast); ast_append(members, ne); ast_set(ast, stringtab("ne"), ne, SYM_DEFINED); if(!ast_passes_subtree(&ne, options, PASS_TRAITS)) r = false; } ast_free_unattached(typeargs); return r; }
// 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; }