bool is_this_incomplete(typecheck_t* t, ast_t* ast) { // If we're in a default argument, we're incomplete by definition. if(t->frame->method == NULL) return true; // If we're not in a constructor, we're complete by definition. if(ast_id(t->frame->method) != TK_NEW) return false; // Check if all fields have been marked as defined. ast_t* members = ast_childidx(t->frame->type, 4); ast_t* member = ast_child(members); while(member != NULL) { switch(ast_id(member)) { case TK_FLET: case TK_FVAR: case TK_EMBED: { sym_status_t status; ast_t* id = ast_child(member); ast_get(ast, ast_name(id), &status); if(status != SYM_DEFINED) return true; break; } default: {} } member = ast_sibling(member); } return false; }
static LLVMValueRef make_fieldptr(compile_t* c, LLVMValueRef l_value, ast_t* l_type, ast_t* right) { switch(ast_id(l_type)) { case TK_NOMINAL: { assert(ast_id(right) == TK_ID); ast_t* def = (ast_t*)ast_data(l_type); ast_t* field = ast_get(def, ast_name(right), NULL); int index = (int)ast_index(field); if(ast_id(def) != TK_STRUCT) index++; if(ast_id(def) == TK_ACTOR) index++; return LLVMBuildStructGEP(c->builder, l_value, index, ""); } case TK_TUPLETYPE: { assert(ast_id(right) == TK_INT); int index = (int)ast_int(right)->low; return LLVMBuildExtractValue(c->builder, l_value, index, ""); } case TK_ARROW: return make_fieldptr(c, l_value, ast_childidx(l_type, 1), right); default: {} } assert(0); return NULL; }
static bool check_fields_defined(ast_t* ast) { assert(ast_id(ast) == TK_NEW); ast_t* members = ast_parent(ast); ast_t* member = ast_child(members); bool result = true; while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: { sym_status_t status; ast_t* id = ast_child(member); ast_t* def = ast_get(ast, ast_name(id), &status); if((def != member) || (status != SYM_DEFINED)) { ast_error(def, "field left undefined in constructor"); result = false; } break; } default: {} } member = ast_sibling(member); } if(!result) ast_error(ast, "constructor with undefined fields is here"); return result; }
static void reify_reference(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; pony_assert(ast_id(ast) == TK_REFERENCE); const char* name = ast_name(ast_child(ast)); sym_status_t status; ast_t* ref_def = ast_get(ast, name, &status); if(ref_def == NULL) return; ast_t* param_def = (ast_t*)ast_data(typeparam); pony_assert(param_def != NULL); if(ref_def != param_def) return; ast_setid(ast, TK_TYPEREF); ast_add(ast, ast_from(ast, TK_NONE)); // 1st child: package reference ast_append(ast, ast_from(ast, TK_NONE)); // 3rd child: type args ast_settype(ast, typearg); }
bool names_nominal(pass_opt_t* opt, ast_t* scope, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; if(ast_data(ast) != NULL) return true; AST_GET_CHILDREN(ast, package_id, type_id, typeparams, cap, eph); // Keep some stats. t->stats.names_count++; if(ast_id(cap) == TK_NONE) t->stats.default_caps_count++; ast_t* r_scope = get_package_scope(scope, ast); if(r_scope == NULL) return false; bool local_package = (r_scope == scope) || (r_scope == ast_nearest(ast, TK_PACKAGE)); // Find our definition. const char* name = ast_name(type_id); ast_t* def = ast_get(r_scope, name, NULL); bool r = true; if(def == NULL) { ast_error(type_id, "can't find definition of '%s'", name); r = false; } else { // Check for a private type. if(!local_package && (name[0] == '_')) { ast_error(type_id, "can't access a private type from another package"); r = false; } switch(ast_id(def)) { case TK_TYPE: r = names_typealias(opt, astp, def); break; case TK_TYPEPARAM: r = names_typeparam(astp, def); break; case TK_INTERFACE: case TK_TRAIT: case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: r = names_type(t, astp, def); break; default: ast_error(type_id, "definition of '%s' is not a type", name); r = false; break; } } return r; }
bool expr_fieldref(pass_opt_t* opt, ast_t* ast, ast_t* find, token_id tid) { AST_GET_CHILDREN(ast, left, right); ast_t* l_type = ast_type(left); if(is_typecheck_error(l_type)) return false; AST_GET_CHILDREN(find, id, f_type, init); // Viewpoint adapted type of the field. ast_t* type = viewpoint_type(l_type, f_type); if(ast_id(type) == TK_ARROW) { ast_t* upper = viewpoint_upper(type); if(upper == NULL) { ast_error(opt->check.errors, ast, "can't read a field through %s", ast_print_type(l_type)); return false; } ast_free_unattached(upper); } // In a recover expression, we can access obj.field if field is sendable // and not being assigned to, even if obj isn't sendable. typecheck_t* t = &opt->check; if(t->frame->recover != NULL) { if(!sendable(type)) { if(!sendable(l_type)) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "can't access field of non-sendable " "object inside of a recover expression"); ast_error_frame(&frame, find, "this would be possible if the field was " "sendable"); errorframe_report(&frame, opt->check.errors); return false; } } else { ast_t* parent = ast_parent(ast); ast_t* current = ast; while(ast_id(parent) != TK_RECOVER && ast_id(parent) != TK_ASSIGN) { current = parent; parent = ast_parent(parent); } if(ast_id(parent) == TK_ASSIGN && ast_child(parent) != current) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "can't access field of non-sendable " "object inside of a recover expression"); ast_error_frame(&frame, parent, "this would be possible if the field " "wasn't assigned to"); errorframe_report(&frame, opt->check.errors); return false; } } } // Set the unadapted field type. ast_settype(right, f_type); // Set the type so that it isn't free'd as unattached. ast_setid(ast, tid); ast_settype(ast, type); if(ast_id(left) == TK_THIS) { // Handle symbol status if the left side is 'this'. const char* name = ast_name(id); sym_status_t status; ast_get(ast, name, &status); if(!valid_reference(opt, ast, type, status)) return false; } return true; }
// Process all field delegations in the given type. // Stage 3. static bool field_delegations(ast_t* entity) { assert(entity != NULL); ast_t* members = ast_childidx(entity, 4); assert(members != NULL); bool r = true; // Check all fields for(ast_t* f = ast_child(members); f != NULL; f = ast_sibling(f)) { if(is_field(f)) { AST_GET_CHILDREN(f, id, f_type, value, delegates); // Check all delegates for field for(ast_t* d = ast_child(delegates); d != NULL; d = ast_sibling(d)) { if(!check_delegate(entity, f_type, d)) { r = false; continue; } // Mark all methods in delegate trait as targets for this field ast_t* trait = (ast_t*)ast_data(d); assert(trait != NULL); ast_t* t_members = ast_childidx(trait, 4); for(ast_t* m = ast_child(t_members); m != NULL; m = ast_sibling(m)) { if(is_method(m)) { // Mark method as delegate target for this field const char* method_name = ast_name(ast_childidx(m, 1)); assert(method_name != NULL); ast_t* local_method = ast_get(entity, method_name, NULL); assert(local_method != NULL); method_t* info = (method_t*)ast_data(local_method); assert(info != NULL); if(info->delegate_field_1 == NULL) { // First delegate field for this method info->delegate_field_1 = f; info->delegate_target_1 = d; } else if(info->delegate_field_2 == NULL && info->delegate_field_1 != f) { // We already have one delegate field, record second info->delegate_field_2 = f; info->delegate_target_2 = d; } } } } } } return r; }
static ast_t* lookup_nominal(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { assert(ast_id(type) == TK_NOMINAL); typecheck_t* t = &opt->check; ast_t* def = (ast_t*)ast_data(type); AST_GET_CHILDREN(def, type_id, typeparams); const char* type_name = ast_name(type_id); if((type_name[0] == '_') && (from != NULL) && (opt != NULL)) { if(ast_nearest(def, TK_PACKAGE) != t->frame->package) { if(errors) { ast_error(from, "can't lookup fields or methods on private types from other packages" ); } return NULL; } } ast_t* find = ast_get(def, name, NULL); if(find != NULL) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: break; case TK_NEW: case TK_BE: case TK_FUN: { // Typecheck default args immediately. if(opt != NULL) { AST_GET_CHILDREN(find, cap, id, typeparams, params); ast_t* param = ast_child(params); while(param != NULL) { AST_GET_CHILDREN(param, name, type, def_arg); if((ast_id(def_arg) != TK_NONE) && (ast_type(def_arg) == NULL)) { ast_settype(def_arg, ast_from(def_arg, TK_INFERTYPE)); if(ast_visit_scope(&def_arg, NULL, pass_expr, opt, PASS_EXPR) != AST_OK) return false; ast_visit_scope(&def_arg, NULL, pass_nodebug, opt, PASS_ALL); } param = ast_sibling(param); } } break; } default: find = NULL; } } if(find == NULL) { if(errors) ast_error(from, "couldn't find '%s' in '%s'", name, type_name); return NULL; } if((name[0] == '_') && (from != NULL) && (opt != NULL)) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: if(t->frame->type != def) { if(errors) { ast_error(from, "can't lookup private fields from outside the type"); } return NULL; } break; case TK_NEW: case TK_BE: case TK_FUN: { if(ast_nearest(def, TK_PACKAGE) != t->frame->package) { if(errors) { ast_error(from, "can't lookup private methods from outside the package"); } return NULL; } break; } default: assert(0); return NULL; } if(!strcmp(name, "_final")) { switch(ast_id(find)) { case TK_NEW: case TK_BE: case TK_FUN: if(errors) ast_error(from, "can't lookup a _final function"); return NULL; default: {} } } } ast_t* typeargs = ast_childidx(type, 2); ast_t* r_find = viewpoint_replacethis(find, orig); ast_t* rr_find = reify(r_find, typeparams, typeargs); ast_free_unattached(r_find); return rr_find; }
static bool is_nominal_sub_interface(ast_t* sub, ast_t* super, errorframe_t* errors) { // implements(N, I) // k <: k' // --- // N k <: I k ast_t* sub_def = (ast_t*)ast_data(sub); ast_t* super_def = (ast_t*)ast_data(super); // Add an assumption: sub <: super if(push_assume(sub, super)) return true; bool ret = true; // A struct has no descriptor, so can't be a subtype of an interface. if(ast_id(sub_def) == TK_STRUCT) { ret = false; if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: a struct can't be a subtype of an " "interface", ast_print_type(sub), ast_print_type(super)); } } if(!is_sub_cap_and_eph(sub, super, errors)) ret = false; ast_t* sub_typeargs = ast_childidx(sub, 2); ast_t* sub_typeparams = ast_childidx(sub_def, 1); ast_t* super_typeargs = ast_childidx(super, 2); ast_t* super_typeparams = ast_childidx(super_def, 1); ast_t* super_members = ast_childidx(super_def, 4); ast_t* super_member = ast_child(super_members); while(super_member != NULL) { ast_t* super_member_id = ast_childidx(super_member, 1); ast_t* sub_member = ast_get(sub_def, ast_name(super_member_id), NULL); // If we don't provide a method, we aren't a subtype. if(sub_member == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: it has no method %s", ast_print_type(sub), ast_print_type(super), ast_name(super_member_id)); } ret = false; super_member = ast_sibling(super_member); continue; } // Reify the method on the subtype. ast_t* r_sub_member = reify(sub_member, sub_typeparams, sub_typeargs); assert(r_sub_member != NULL); // Reify the method on the supertype. ast_t* r_super_member = reify(super_member, super_typeparams, super_typeargs); assert(r_super_member != NULL); // Check the reified methods. bool ok = is_fun_sub_fun(r_sub_member, r_super_member, errors); ast_free_unattached(r_sub_member); ast_free_unattached(r_super_member); if(!ok) { ret = false; if(errors != NULL) { ast_error_frame(errors, sub_member, "%s is not a subtype of %s: method %s has an incompatible signature", ast_print_type(sub), ast_print_type(super), ast_name(super_member_id)); } } super_member = ast_sibling(super_member); } pop_assume(); return ret; }
bool genexe(compile_t* c, ast_t* program) { errors_t* errors = c->opt->check.errors; // The first package is the main package. It has to have a Main actor. const char* main_actor = c->str_Main; const char* env_class = c->str_Env; ast_t* package = ast_child(program); ast_t* main_def = ast_get(package, main_actor, NULL); if(main_def == NULL) { errorf(errors, NULL, "no Main actor found in package '%s'", c->filename); return false; } // Generate the Main actor and the Env class. ast_t* main_ast = type_builtin(c->opt, main_def, main_actor); ast_t* env_ast = type_builtin(c->opt, main_def, env_class); if(lookup(c->opt, main_ast, main_ast, c->str_create) == NULL) return false; if(c->opt->verbosity >= VERBOSITY_INFO) fprintf(stderr, " Reachability\n"); reach(c->reach, main_ast, c->str_create, NULL, c->opt); reach(c->reach, env_ast, c->str__create, NULL, c->opt); if(c->opt->limit == PASS_REACH) return true; if(c->opt->verbosity >= VERBOSITY_INFO) fprintf(stderr, " Selector painting\n"); paint(&c->reach->types); if(c->opt->limit == PASS_PAINT) return true; if(!gentypes(c)) return false; if(c->opt->verbosity >= VERBOSITY_ALL) reach_dump(c->reach); reach_type_t* t_main = reach_type(c->reach, main_ast); reach_type_t* t_env = reach_type(c->reach, env_ast); if((t_main == NULL) || (t_env == NULL)) return false; gen_main(c, t_main, t_env); if(!genopt(c, true)) return false; if(c->opt->runtimebc) { if(!codegen_merge_runtime_bitcode(c)) return false; // Rerun the optimiser without the Pony-specific optimisation passes. // Inlining runtime functions can screw up these passes so we can't // run the optimiser only once after merging. if(!genopt(c, false)) return false; } const char* file_o = genobj(c); if(file_o == NULL) return false; if(c->opt->limit < PASS_ALL) return true; if(!link_exe(c, program, file_o)) return false; #ifdef PLATFORM_IS_WINDOWS _unlink(file_o); #else unlink(file_o); #endif return true; }
static bool is_fun_sub_fun(ast_t* sub, ast_t* super, ast_t* isub, ast_t* isuper) { token_id tsub = ast_id(sub); token_id tsuper = ast_id(super); switch(tsub) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } switch(tsuper) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } // A constructor can only be a subtype of a constructor. if(((tsub == TK_NEW) || (tsuper == TK_NEW)) && (tsub != tsuper)) return false; AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params); // Must have the same name. if(ast_name(sub_id) != ast_name(super_id)) return false; // Must have the same number of type parameters and parameters. if((ast_childcount(sub_typeparams) != ast_childcount(super_typeparams)) || (ast_childcount(sub_params) != ast_childcount(super_params))) return false; ast_t* r_sub = sub; if(ast_id(super_typeparams) != TK_NONE) { // Reify sub with the type parameters of super. BUILD(typeargs, super_typeparams, NODE(TK_TYPEARGS)); ast_t* super_typeparam = ast_child(super_typeparams); while(super_typeparam != NULL) { AST_GET_CHILDREN(super_typeparam, super_id, super_constraint); token_id cap = cap_from_constraint(super_constraint); BUILD(typearg, super_typeparam, NODE(TK_TYPEPARAMREF, TREE(super_id) NODE(cap) NONE)); ast_t* def = ast_get(super_typeparam, ast_name(super_id), NULL); ast_setdata(typearg, def); ast_append(typeargs, typearg); super_typeparam = ast_sibling(super_typeparam); } r_sub = reify(sub, sub, sub_typeparams, typeargs); ast_free_unattached(typeargs); } bool ok = is_reified_fun_sub_fun(r_sub, super, isub, isuper); if(r_sub != sub) ast_free_unattached(r_sub); return ok; }
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* opt) { pony_assert(from != NULL); magic_package_t* magic = find_magic_package(path, opt); const char* full_path = path; const char* qualified_name = path; ast_t* program = ast_nearest(from, TK_PROGRAM); if(magic == NULL) { // Lookup (and hence normalise) path bool is_relative = false; bool found_notdir = false; full_path = find_path(from, path, &is_relative, &found_notdir, opt); if(full_path == NULL) { errorf(opt->check.errors, path, "couldn't locate this path"); if(found_notdir) errorf_continue(opt->check.errors, path, "note that a compiler " "invocation or a 'use' directive must refer to a directory"); return NULL; } if((from != program) && is_relative) { // Package to load is relative to from, build the qualified name // The qualified name should be relative to the program being built package_t* from_pkg = (package_t*)ast_data(ast_child(program)); if(from_pkg != NULL) { const char* base_name = from_pkg->qualified_name; size_t base_name_len = strlen(base_name); size_t path_len = strlen(path); size_t len = base_name_len + path_len + 2; char* q_name = (char*)ponyint_pool_alloc_size(len); memcpy(q_name, base_name, base_name_len); q_name[base_name_len] = '/'; memcpy(q_name + base_name_len + 1, path, path_len); q_name[len - 1] = '\0'; qualified_name = stringtab_consume(q_name, len); } } // we are loading the package specified as program dir if(from == program) { // construct the qualified name from the basename of the full path const char* basepath = strrchr(full_path, '/'); if(basepath == NULL) { basepath = full_path; } else { basepath = basepath + 1; } qualified_name = basepath; } } ast_t* package = ast_get(program, full_path, NULL); // Package already loaded if(package != NULL) return package; package = create_package(program, full_path, qualified_name, opt); if(opt->verbosity >= VERBOSITY_INFO) fprintf(stderr, "Building %s -> %s\n", path, full_path); if(magic != NULL) { if(magic->src != NULL) { if(!parse_source_code(package, magic->src, opt)) return NULL; } else if(magic->mapped_path != NULL) { if(!parse_files_in_dir(package, magic->mapped_path, opt)) return NULL; } else { return NULL; } } else { if(!parse_files_in_dir(package, full_path, opt)) return NULL; } if(ast_child(package) == NULL) { ast_error(opt->check.errors, package, "no source files in package '%s'", path); return NULL; } if(!ast_passes_subtree(&package, opt, opt->program_pass)) { // If these passes failed, don't run future passes. ast_setflag(package, AST_FLAG_PRESERVE); return NULL; } return package; }
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* options) { const char* magic = find_magic_package(path); const char* full_path = path; const char* qualified_name = path; ast_t* program = ast_nearest(from, TK_PROGRAM); if(magic == NULL) { // Lookup (and hence normalise) path bool is_relative = false; full_path = find_path(from, path, &is_relative); if(full_path == NULL) return NULL; if((from != NULL) && is_relative) { // Package to load is relative to from, build the qualified name // The qualified name should be relative to the program being built package_t* from_pkg = (package_t*)ast_data(ast_child(program)); if(from_pkg != NULL) { const char* base_name = from_pkg->qualified_name; size_t base_name_len = strlen(base_name); size_t path_len = strlen(path); size_t len = base_name_len + path_len + 2; char* q_name = (char*)pool_alloc_size(len); memcpy(q_name, base_name, base_name_len); q_name[base_name_len] = '/'; memcpy(q_name + base_name_len + 1, path, path_len); q_name[len - 1] = '\0'; qualified_name = stringtab_consume(q_name, len); } } } ast_t* package = ast_get(program, full_path, NULL); // Package already loaded if(package != NULL) return package; package = create_package(program, full_path, qualified_name); if(report_build) printf("Building %s -> %s\n", path, full_path); if(magic != NULL) { if(!parse_source_code(package, magic, options)) return NULL; } else { if(!parse_files_in_dir(package, full_path, options)) return NULL; } if(ast_child(package) == NULL) { ast_error(package, "no source files in package '%s'", path); return NULL; } // We add new packages to the end of the program, so they will be reached by // the current pass processing. This means we need to catch up the new // package to the previous pass if(!ast_passes_subtree(&package, options, pass_prev(options->type_catchup_pass))) return NULL; return package; }
bool genexe(compile_t* c, ast_t* program) { errors_t* errors = c->opt->check.errors; // The first package is the main package. It has to have a Main actor. const char* main_actor = c->str_Main; const char* env_class = c->str_Env; ast_t* package = ast_child(program); ast_t* main_def = ast_get(package, main_actor, NULL); if(main_def == NULL) { errorf(errors, NULL, "no Main actor found in package '%s'", c->filename); return false; } // Generate the Main actor and the Env class. ast_t* main_ast = type_builtin(c->opt, main_def, main_actor); ast_t* env_ast = type_builtin(c->opt, main_def, env_class); if(lookup(NULL, main_ast, main_ast, c->str_create) == NULL) return false; if(c->opt->verbosity >= VERBOSITY_INFO) fprintf(stderr, " Reachability\n"); reach(c->reach, main_ast, c->str_create, NULL, c->opt); reach(c->reach, env_ast, c->str__create, NULL, c->opt); if(c->opt->limit == PASS_REACH) return true; if(c->opt->verbosity >= VERBOSITY_INFO) fprintf(stderr, " Selector painting\n"); paint(&c->reach->types); if(c->opt->limit == PASS_PAINT) return true; if(!gentypes(c)) return false; if(c->opt->verbosity >= VERBOSITY_ALL) reach_dump(c->reach); reach_type_t* t_main = reach_type(c->reach, main_ast); reach_type_t* t_env = reach_type(c->reach, env_ast); if((t_main == NULL) || (t_env == NULL)) return false; gen_main(c, t_main, t_env); if(!genopt(c)) return false; const char* file_o = genobj(c); if(file_o == NULL) return false; if(c->opt->limit < PASS_ALL) return true; if(!link_exe(c, program, file_o)) return false; #ifdef PLATFORM_IS_WINDOWS _unlink(file_o); #else unlink(file_o); #endif return true; }
static bool assign_id(typecheck_t* t, ast_t* ast, bool let, bool need_value) { assert(ast_id(ast) == TK_ID); const char* name = ast_name(ast); sym_status_t status; ast_get(ast, name, &status); switch(status) { case SYM_UNDEFINED: if(need_value) { ast_error(ast, "the left side is undefined but its value is used"); return false; } ast_setstatus(ast, name, SYM_DEFINED); return true; case SYM_DEFINED: if(let) { ast_error(ast, "can't assign to a let or embed definition more than once"); return false; } return true; case SYM_CONSUMED: { bool ok = true; if(need_value) { ast_error(ast, "the left side is consumed but its value is used"); ok = false; } if(let) { ast_error(ast, "can't assign to a let or embed definition more than once"); ok = false; } if(t->frame->try_expr != NULL) { ast_error(ast, "can't reassign to a consumed identifier in a try expression"); ok = false; } if(ok) ast_setstatus(ast, name, SYM_DEFINED); return ok; } default: {} } assert(0); return false; }
// 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; }
// Process the given capture and create the AST for the corresponding field. // Returns the create field AST in out_field, which must be freed by the caller, // or NULL if there is no field to create. // Returns false on error. static bool make_capture_field(pass_opt_t* opt, ast_t* capture, ast_t** out_field) { pony_assert(capture != NULL); pony_assert(out_field != NULL); AST_GET_CHILDREN(capture, id_node, type, value); const char* name = ast_name(id_node); bool is_dontcare = is_name_dontcare(name); // There are 3 varieties of capture: // x -> capture variable x, type from defn of x // x = y -> capture expression y, type inferred from expression type // x: T = y -> capture expression y, type T if(ast_id(value) == TK_NONE) { // Variable capture pony_assert(ast_id(type) == TK_NONE); if(is_dontcare) { *out_field = NULL; return true; } ast_t* def = ast_get(capture, name, NULL); if(def == NULL) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", variable not defined", name); return false; } // lambda captures used before their declaration with their type // not defined are not legal if(!def_before_use(opt, def, capture, name)) return false; switch(ast_id(def)) { case TK_VAR: case TK_LET: case TK_PARAM: case TK_MATCH_CAPTURE: case TK_FVAR: case TK_FLET: case TK_EMBED: break; default: ast_error(opt->check.errors, id_node, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return false; } BUILD(capture_rhs, id_node, NODE(TK_REFERENCE, ID(name))); type = alias(ast_type(def)); value = capture_rhs; } else if(ast_id(type) == TK_NONE) { // No type specified, use type of the captured expression if(ast_type(value) == NULL) return false; type = alias(ast_type(value)); } else { // Type given, infer literals if(!coerce_literals(&value, type, opt)) return false; // Check subtyping now if we're capturing into '_', since the field will be // discarded. if(is_dontcare) { ast_t* v_type = alias(ast_type(value)); errorframe_t info = NULL; if(!is_subtype(v_type, type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, value, "argument not a subtype of parameter"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ast_free_unattached(v_type); return false; } ast_free_unattached(v_type); } } if(is_typecheck_error(type)) return false; if(is_dontcare) { *out_field = NULL; return true; } type = sanitise_type(type); BUILD(field, id_node, NODE(TK_FVAR, TREE(id_node) TREE(type) TREE(value))); *out_field = field; return true; }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->def_arg != NULL) { ast_error(opt->check.errors, ast, "can't reference 'this' in a default argument"); return false; } sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(opt->check.errors, ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) { ast_t* parent = ast_parent(ast); if(ast_id(parent) != TK_DOT) cap = TK_TAG; } bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); type = arrow; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(opt->check.errors, ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(opt->check.errors, ast, "couldn't create a type for 'this'"); ast_free(type); return false; } // Unless this is a field lookup, treat an incomplete `this` as a tag. ast_t* parent = ast_parent(ast); bool incomplete_ok = false; if((ast_id(parent) == TK_DOT) && (ast_child(parent) == ast)) { ast_t* right = ast_sibling(ast); assert(ast_id(right) == TK_ID); ast_t* find = lookup_try(opt, ast, nominal, ast_name(right)); if(find != NULL) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: incomplete_ok = true; break; default: {} } } } if(!incomplete_ok && is_this_incomplete(t, ast)) { ast_t* tag_type = set_cap_and_ephemeral(nominal, TK_TAG, TK_NONE); ast_replace(&nominal, tag_type); } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->def_arg != NULL) { ast_error(ast, "can't reference 'this' in a default argument"); return false; } sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) cap = TK_TAG; bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); type = arrow; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }
// Process the given capture and create the AST for the corresponding field. // Returns the create field AST, which must be freed by the caller. // Returns NULL on error. static ast_t* make_capture_field(pass_opt_t* opt, ast_t* capture) { assert(capture != NULL); AST_GET_CHILDREN(capture, id_node, type, value); const char* name = ast_name(id_node); // There are 3 varieties of capture: // x -> capture variable x, type from defn of x // x = y -> capture expression y, type inferred from expression type // x: T = y -> capture expression y, type T if(ast_id(value) == TK_NONE) { // Variable capture assert(ast_id(type) == TK_NONE); ast_t* def = ast_get(capture, name, NULL); if(def == NULL) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", variable not defined", name); return NULL; } token_id def_id = ast_id(def); if(def_id != TK_ID && def_id != TK_FVAR && def_id != TK_FLET && def_id != TK_PARAM) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } BUILD(capture_rhs, id_node, NODE(TK_REFERENCE, ID(name))); type = alias(ast_type(def)); value = capture_rhs; } else if(ast_id(type) == TK_NONE) { // No type specified, use type of the captured expression type = alias(ast_type(value)); } else { // Type given, infer literals if(!coerce_literals(&value, type, opt)) return NULL; } if(is_typecheck_error(type)) return NULL; type = sanitise_type(type); BUILD(field, id_node, NODE(TK_FVAR, TREE(id_node) TREE(type) TREE(value) NONE)); // Delegate type return field; }
static bool capture_from_reference(pass_opt_t* opt, ast_t* ctx, ast_t* ast, ast_t* captures, ast_t** last_capture) { const char* name = ast_name(ast_child(ast)); if(is_name_dontcare(name)) return true; ast_t* refdef = ast_get(ast, name, NULL); if(refdef != NULL) return true; refdef = ast_get(ctx, name, NULL); if(refdef == NULL) { ast_error(opt->check.errors, ast, "cannot capture \"%s\", variable not defined", name); return false; } if(!def_before_use(opt, refdef, ctx, name)) return false; switch(ast_id(refdef)) { case TK_VAR: case TK_LET: case TK_PARAM: case TK_MATCH_CAPTURE: case TK_FVAR: case TK_FLET: case TK_EMBED: break; default: ast_error(opt->check.errors, ast, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } // Check if we've already captured it for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { AST_GET_CHILDREN(p, c_name, c_type); if(name == ast_name(c_name)) return true; } ast_t* type = alias(ast_type(refdef)); if(is_typecheck_error(type)) return false; type = sanitise_type(type); BUILD(field, ast, NODE(TK_FVAR, ID(name) TREE(type) NODE(TK_REFERENCE, ID(name)))); ast_list_append(captures, last_capture, field); return true; }
/** * 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; }
bool genexe(compile_t* c, ast_t* program) { // The first package is the main package. It has to have a Main actor. const char* main_actor = stringtab("Main"); const char* env_class = stringtab("Env"); ast_t* package = ast_child(program); ast_t* main_def = ast_get(package, main_actor, NULL); if(main_def == NULL) { errorf(NULL, "no Main actor found in package '%s'", c->filename); return false; } // Generate the Main actor and the Env class. ast_t* main_ast = type_builtin(c->opt, main_def, main_actor); ast_t* env_ast = type_builtin(c->opt, main_def, env_class); const char* create = stringtab("create"); if(lookup(NULL, main_ast, main_ast, create) == NULL) return false; genprim_reachable_init(c, program); reach(c->reachable, main_ast, create, NULL); reach(c->reachable, env_ast, stringtab("_create"), NULL); paint(c->reachable); gentype_t main_g; gentype_t env_g; bool ok = gentype(c, main_ast, &main_g) && gentype(c, env_ast, &env_g); if(ok) gen_main(c, &main_g, &env_g); ast_free_unattached(main_ast); ast_free_unattached(env_ast); if(!ok) return false; if(!genopt(c)) return false; const char* file_o = genobj(c); if(file_o == NULL) return false; if(c->opt->limit < PASS_ALL) return true; if(!link_exe(c, program, file_o)) return false; #ifdef PLATFORM_IS_WINDOWS _unlink(file_o); #else unlink(file_o); #endif return true; }
bool expr_reference(pass_opt_t* opt, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; // Everything we reference must be in scope. const char* name = ast_name(ast_child(ast)); sym_status_t status; ast_t* def = ast_get(ast, name, &status); if(def == NULL) { const char* alt_name = suggest_alt_name(ast, name); if(alt_name == NULL) ast_error(ast, "can't find declaration of '%s'", name); else ast_error(ast, "can't find declaration of '%s', did you mean '%s'?", name, alt_name); return false; } switch(ast_id(def)) { case TK_PACKAGE: { // Only allowed if in a TK_DOT with a type. if(ast_id(ast_parent(ast)) != TK_DOT) { ast_error(ast, "a package can only appear as a prefix to a type"); return false; } ast_setid(ast, TK_PACKAGEREF); return true; } case TK_INTERFACE: case TK_TRAIT: case TK_TYPE: case TK_TYPEPARAM: case TK_PRIMITIVE: case TK_STRUCT: case TK_CLASS: case TK_ACTOR: { // It's a type name. This may not be a valid type, since it may need // type arguments. ast_t* id = ast_child(def); const char* name = ast_name(id); ast_t* type = type_sugar(ast, NULL, name); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_FVAR: case TK_FLET: case TK_EMBED: { // Transform to "this.f". if(!def_before_use(def, ast, name)) return false; ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_child(ast)); ast_t* self = ast_from(ast, TK_THIS); ast_add(dot, self); ast_replace(astp, dot); if(!expr_this(opt, self)) return false; return expr_dot(opt, astp); } case TK_PARAM: { if(t->frame->def_arg != NULL) { ast_error(ast, "can't reference a parameter in a default argument"); return false; } if(!def_before_use(def, ast, name)) return false; ast_t* type = ast_type(def); if(is_typecheck_error(type)) return false; if(!valid_reference(opt, ast, type, status)) return false; if(!sendable(type) && (t->frame->recover != NULL)) { ast_error(ast, "can't access a non-sendable parameter from inside a recover " "expression"); return false; } // Get the type of the parameter and attach it to our reference. // Automatically consume a parameter if the function is done. ast_t* r_type = type; if(is_method_return(t, ast)) r_type = consume_type(type, TK_NONE); ast_settype(ast, r_type); ast_setid(ast, TK_PARAMREF); return true; } case TK_NEW: case TK_BE: case TK_FUN: { // Transform to "this.f". ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_child(ast)); ast_t* self = ast_from(ast, TK_THIS); ast_add(dot, self); ast_replace(astp, dot); if(!expr_this(opt, self)) return false; return expr_dot(opt, astp); } case TK_ID: { if(!def_before_use(def, ast, name)) return false; ast_t* type = ast_type(def); if(type != NULL && ast_id(type) == TK_INFERTYPE) { ast_error(ast, "cannot infer type of %s\n", ast_nice_name(def)); ast_settype(def, ast_from(def, TK_ERRORTYPE)); ast_settype(ast, ast_from(ast, TK_ERRORTYPE)); return false; } if(is_typecheck_error(type)) return false; if(!valid_reference(opt, ast, type, status)) return false; ast_t* var = ast_parent(def); switch(ast_id(var)) { case TK_VAR: ast_setid(ast, TK_VARREF); break; case TK_LET: case TK_MATCH_CAPTURE: ast_setid(ast, TK_LETREF); break; default: assert(0); return false; } if(!sendable(type)) { if(t->frame->recover != NULL) { ast_t* def_recover = ast_nearest(def, TK_RECOVER); if(t->frame->recover != def_recover) { ast_error(ast, "can't access a non-sendable local defined outside " "of a recover expression from within that recover expression"); return false; } } } // Get the type of the local and attach it to our reference. // Automatically consume a local if the function is done. ast_t* r_type = type; if(is_method_return(t, ast)) r_type = consume_type(type, TK_NONE); ast_settype(ast, r_type); return true; } default: {} } assert(0); return false; }
static ast_t* lookup_nominal(typecheck_t* t, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { assert(ast_id(type) == TK_NOMINAL); ast_t* def = (ast_t*)ast_data(type); ast_t* type_name = ast_child(def); ast_t* find = ast_get(def, name, NULL); if(find != NULL) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_NEW: case TK_BE: case TK_FUN: break; default: find = NULL; } } if(find == NULL) { if(errors) ast_error(from, "couldn't find '%s' in '%s'", name, ast_name(type_name)); return NULL; } if((name[0] == '_') && (from != NULL) && (t != NULL)) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: if(t->frame->type != def) { if(errors) { ast_error(from, "can't lookup private fields from outside the type"); } return NULL; } break; case TK_NEW: case TK_BE: case TK_FUN: { if(ast_nearest(def, TK_PACKAGE) != t->frame->package) { if(errors) { ast_error(from, "can't lookup private methods from outside the package"); } return NULL; } break; } default: assert(0); return NULL; } if(!strcmp(name, "_final")) { switch(ast_id(find)) { case TK_NEW: case TK_BE: case TK_FUN: if(errors) ast_error(from, "can't lookup a _final function"); return NULL; default: {} } } } ast_t* typeparams = ast_sibling(type_name); ast_t* typeargs = ast_childidx(type, 2); find = ast_dup(find); orig = ast_dup(orig); replace_thistype(&find, orig); ast_free_unattached(orig); ast_t* r_find = reify(from, find, typeparams, typeargs); if(r_find != find) { ast_free_unattached(find); find = r_find; } if((find != NULL) && !flatten_arrows(&find, errors)) { if(errors) ast_error(from, "can't look this up on a tag"); ast_free_unattached(find); return NULL; } return find; }
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* options) { assert(from != NULL); const char* magic = find_magic_package(path); const char* full_path = path; const char* qualified_name = path; ast_t* program = ast_nearest(from, TK_PROGRAM); if(magic == NULL) { // Lookup (and hence normalise) path bool is_relative = false; full_path = find_path(from, path, &is_relative); if(full_path == NULL) return NULL; if((from != program) && is_relative) { // Package to load is relative to from, build the qualified name // The qualified name should be relative to the program being built package_t* from_pkg = (package_t*)ast_data(ast_child(program)); if(from_pkg != NULL) { const char* base_name = from_pkg->qualified_name; size_t base_name_len = strlen(base_name); size_t path_len = strlen(path); size_t len = base_name_len + path_len + 2; char* q_name = (char*)pool_alloc_size(len); memcpy(q_name, base_name, base_name_len); q_name[base_name_len] = '/'; memcpy(q_name + base_name_len + 1, path, path_len); q_name[len - 1] = '\0'; qualified_name = stringtab_consume(q_name, len); } } } ast_t* package = ast_get(program, full_path, NULL); // Package already loaded if(package != NULL) return package; package = create_package(program, full_path, qualified_name); if(report_build) printf("Building %s -> %s\n", path, full_path); if(magic != NULL) { if(!parse_source_code(package, magic, options)) return NULL; } else { if(!parse_files_in_dir(package, full_path, options)) return NULL; } if(ast_child(package) == NULL) { ast_error(package, "no source files in package '%s'", path); return NULL; } if(!ast_passes_subtree(&package, options, options->program_pass)) { // If these passes failed, don't run future passes. ast_setflag(package, AST_FLAG_PRESERVE); return NULL; } return package; }
static bool is_fun_sub_fun(ast_t* sub, ast_t* super, errorframe_t* errors) { token_id tsub = ast_id(sub); token_id tsuper = ast_id(super); switch(tsub) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } switch(tsuper) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params); // Must have the same name. if(ast_name(sub_id) != ast_name(super_id)) { if(errors != NULL) { ast_error_frame(errors, sub, "method %s is not a subtype of method %s: they have different names", ast_name(sub_id), ast_name(super_id)); } return false; } // A constructor can only be a subtype of a constructor. if(((tsub == TK_NEW) || (tsuper == TK_NEW)) && (tsub != tsuper)) { if(errors != NULL) ast_error_frame(errors, sub, "only a constructor can be a subtype of a constructor"); return false; } // Must have the same number of type parameters. if(ast_childcount(sub_typeparams) != ast_childcount(super_typeparams)) { if(errors != NULL) ast_error_frame(errors, sub, "methods have a different number of type parameters"); return false; } // Must have the same number of parameters. if(ast_childcount(sub_params) != ast_childcount(super_params)) { if(errors != NULL) ast_error_frame(errors, sub, "methods have a different number of parameters"); return false; } ast_t* r_sub = sub; if(ast_id(super_typeparams) != TK_NONE) { // Reify sub with the type parameters of super. BUILD(typeargs, super_typeparams, NODE(TK_TYPEARGS)); ast_t* super_typeparam = ast_child(super_typeparams); while(super_typeparam != NULL) { AST_GET_CHILDREN(super_typeparam, super_id, super_constraint); BUILD(typearg, super_typeparam, NODE(TK_TYPEPARAMREF, TREE(super_id) NONE NONE)); ast_t* def = ast_get(super_typeparam, ast_name(super_id), NULL); ast_setdata(typearg, def); typeparam_set_cap(typearg); ast_append(typeargs, typearg); super_typeparam = ast_sibling(super_typeparam); } r_sub = reify(sub, sub_typeparams, typeargs); ast_free_unattached(typeargs); } bool ok = is_reified_fun_sub_fun(r_sub, super, errors); if(r_sub != sub) ast_free_unattached(r_sub); return ok; }