// Write the TQFN for the given type to a new buffer. // By default the type name is taken from the given AST, however this can be // overridden by the type_name parameter. Pass NULL to use the default. // The returned buffer must be freed using pool_free_size when no longer // needed. Note that the size reported is the size of the buffer and includes a // terminator. static char* write_tqfn(ast_t* type, const char* type_name, size_t* out_size) { assert(type != NULL); assert(out_size != NULL); ast_t* package = ast_nearest(type, TK_PACKAGE); assert(package != NULL); // We need the qualified package name and the type name const char* pkg_qual_name = package_qualified_name(package); if(type_name == NULL) type_name = ast_name(ast_child(type)); assert(pkg_qual_name != NULL); assert(type_name != NULL); char* buffer = doc_cat(pkg_qual_name, "-", type_name, "", "", out_size); // Change slashes to dashes for(char* p = buffer; *p != '\0'; p++) { if(*p == '/') *p = '-'; } return buffer; }
static bool is_receiver_safe(typecheck_t* t, ast_t* ast) { switch(ast_id(ast)) { case TK_THIS: case TK_FLETREF: case TK_FVARREF: case TK_EMBEDREF: case TK_PARAMREF: case TK_TUPLEELEMREF: { ast_t* type = ast_type(ast); return sendable(type); } case TK_LETREF: case TK_VARREF: { ast_t* def = (ast_t*)ast_data(ast); pony_assert(def != NULL); ast_t* def_recover = ast_nearest(def, TK_RECOVER); if(t->frame->recover == def_recover) return true; ast_t* type = ast_type(ast); return sendable(type); } default: // Unsafe receivers inside expressions are catched before we get there. return true; } }
static bool is_receiver_safe(typecheck_t* t, ast_t* ast) { switch(ast_id(ast)) { case TK_THIS: case TK_FLETREF: case TK_FVARREF: case TK_EMBEDREF: case TK_PARAMREF: { ast_t* type = ast_type(ast); return sendable(type); } case TK_LETREF: case TK_VARREF: { const char* name = ast_name(ast_child(ast)); sym_status_t status; ast_t* def = ast_get(ast, name, &status); ast_t* def_recover = ast_nearest(def, TK_RECOVER); if(t->frame->recover == def_recover) return true; ast_t* type = ast_type(ast); return sendable(type); } default: // Unsafe receivers inside expressions are catched before we get there. return true; } }
// Process the given provided method for the given entity. // The method passed should be reified already and will be freed by this // function. static bool provided_method(ast_t* entity, ast_t* reified_method, ast_t* raw_method, ast_t** last_method) { assert(entity != NULL); assert(reified_method != NULL); assert(last_method != NULL); const char* entity_name = ast_name(ast_child(entity)); if(ast_id(reified_method) == TK_BE || ast_id(reified_method) == TK_NEW) { // Modify return type to the inheritting type ast_t* ret_type = ast_childidx(reified_method, 4); assert(ast_id(ret_type) == TK_NOMINAL); const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE)); ast_set_name(ast_childidx(ret_type, 0), pkg_name); ast_set_name(ast_childidx(ret_type, 1), entity_name); } // Ignore docstring ast_t* doc = ast_childidx(reified_method, 7); if(ast_id(doc) == TK_STRING) { ast_set_name(doc, ""); ast_setid(doc, TK_NONE); } // Check for existing method of the same name const char* name = ast_name(ast_childidx(reified_method, 1)); assert(name != NULL); ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL && is_field(existing)) { ast_error(existing, "field '%s' clashes with provided method", name); ast_error(raw_method, "method is defined here"); ast_free_unattached(reified_method); return false; } existing = add_method(entity, existing, reified_method, last_method); if(existing == NULL) { ast_free_unattached(reified_method); return false; } method_t* info = (method_t*)ast_data(existing); assert(info != NULL); if(!record_default_body(reified_method, raw_method, info)) ast_free_unattached(reified_method); return true; }
static const char* nominal_name(ast_t* ast) { AST_GET_CHILDREN(ast, package, name, typeargs); ast_t* def = (ast_t*)ast_data(ast); ast_t* pkg = ast_nearest(def, TK_PACKAGE); const char* s = package_symbol(pkg); return build_name(s, ast_name(name), typeargs, false); }
static void type_append(printbuf_t* buf, ast_t* type, bool first) { switch(ast_id(type)) { case TK_UNIONTYPE: { // u3_Arg1_Arg2_Arg3 printbuf(buf, "u%d", ast_childcount(type)); types_append(buf, type); return; } case TK_ISECTTYPE: { // i3_Arg1_Arg2_Arg3 printbuf(buf, "i%d", ast_childcount(type)); types_append(buf, type); return; } case TK_TUPLETYPE: { // t3_Arg1_Arg2_Arg3 printbuf(buf, "t%d", ast_childcount(type)); types_append(buf, type); return; } case TK_NOMINAL: { // pkg_Type[_Arg1_Arg2]_cap AST_GET_CHILDREN(type, package, name, typeargs, cap, eph); ast_t* def = (ast_t*)ast_data(type); ast_t* pkg = ast_nearest(def, TK_PACKAGE); const char* pkg_name = package_symbol(pkg); if(pkg_name != NULL) printbuf(buf, "%s_", pkg_name); printbuf(buf, "%s", ast_name(name)); types_append(buf, typeargs); if(!first) printbuf(buf, "_%s", ast_get_print(cap)); return; } default: {} } assert(0); }
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* options) { const char* magic = find_magic_package(path); const char* name = path; if(magic == NULL) { // Lookup (and hence normalise) path name = find_path(from, path); if(name == NULL) return NULL; } ast_t* program = ast_nearest(from, TK_PROGRAM); ast_t* package = ast_get(program, name, NULL); if(package != NULL) // Package already loaded return package; package = create_package(program, name); if(report_build) printf("Building %s\n", path); if(magic != NULL) { if(!parse_source_code(package, magic, options)) return NULL; } else { if(!parse_files_in_dir(package, name, options)) return NULL; } if(ast_child(package) == NULL) { ast_error(package, "no source files in package '%s'", path); return NULL; } if(!package_passes(package, options)) return NULL; return package; }
/// Process a "path:" scheme use command. bool use_path(ast_t* use, const char* locator, ast_t* name, pass_opt_t* options) { (void)name; const char* libpath = quoted_locator(options, use, locator); if(libpath == NULL) return false; ast_t* p = ast_nearest(use, TK_PROGRAM); program_t* prog = (program_t*)ast_data(p); pony_assert(prog->lib_args == NULL); // Not yet built args if(strlist_find(prog->libpaths, libpath) != NULL) // Ignore duplicate return true; prog->libpaths = strlist_append(prog->libpaths, libpath); return true; }
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { typecheck_t* t = &options->check; ast_t* module = ast_nearest(*ast, TK_MODULE); ast_t* package = ast_parent(module); assert(module != NULL); assert(package != NULL); frame_push(t, NULL); frame_push(t, package); frame_push(t, module); ast_result_t ret = ast_visit(ast, pre, post, options, pass); frame_pop(t); frame_pop(t); frame_pop(t); return ret; }
// Attempt to find the specified package directory in our search path // @return The resulting directory path, which should not be deleted and is // valid indefinitely. NULL is directory cannot be found. static const char* find_path(ast_t* from, const char* path) { // First check for an absolute path if(is_path_absolute(path)) return try_path(NULL, path); const char* result; if((from == NULL) || (ast_id(from) == TK_PROGRAM)) { // Try a path relative to the current working directory result = try_path(NULL, path); if(result != NULL) return result; } else { // Try a path relative to the importing package from = ast_nearest(from, TK_PACKAGE); package_t* pkg = (package_t*)ast_data(from); result = try_path(pkg->path, path); if(result != NULL) return result; } // Try the search paths for(strlist_t* p = search; p != NULL; p = strlist_next(p)) { result = try_path(strlist_data(p), path); if(result != NULL) return result; } errorf(path, "couldn't locate this path"); return NULL; }
static ast_t* get_package_scope(ast_t* scope, ast_t* ast) { assert(ast_id(ast) == TK_NOMINAL); ast_t* package_id = ast_child(ast); // Find our actual package. if(ast_id(package_id) != TK_NONE) { const char* name = ast_name(package_id); if(name[0] == '$') scope = ast_get(ast_nearest(scope, TK_PROGRAM), name, NULL); else scope = ast_get(scope, name, NULL); if((scope == NULL) || (ast_id(scope) != TK_PACKAGE)) { ast_error(package_id, "can't find package '%s'", name); return NULL; } } return scope; }
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_result_t sugar_object(pass_opt_t* opt, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; ast_result_t r = AST_OK; AST_GET_CHILDREN(ast, cap, provides, members); ast_t* c_id = ast_from_string(ast, package_hygienic_id(t)); ast_t* t_params; ast_t* t_args; collect_type_params(ast, &t_params, &t_args); // Create a new anonymous type. BUILD(def, ast, NODE(TK_CLASS, AST_SCOPE TREE(c_id) TREE(t_params) NONE TREE(provides) NODE(TK_MEMBERS) NONE NONE)); // We will have a create method in the type. BUILD_NO_DEBUG(create, members, NODE(TK_NEW, AST_SCOPE NONE ID("create") NONE NONE NONE NONE NODE(TK_SEQ) NONE NONE)); BUILD(type_ref, ast, NODE(TK_REFERENCE, TREE(c_id))); if(ast_id(t_args) != TK_NONE) { // Need to add type args to our type reference BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args))); type_ref = t; } ast_free_unattached(t_args); // We will replace object..end with $0.create(...) BUILD(call, ast, NODE(TK_CALL, NONE NONE NODE(TK_DOT, TREE(type_ref) ID("create")))); ast_t* create_params = ast_childidx(create, 3); ast_t* create_body = ast_childidx(create, 6); ast_t* call_args = ast_child(call); ast_t* class_members = ast_childidx(def, 4); ast_t* member = ast_child(members); bool has_fields = false; bool has_behaviours = false; while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: case TK_EMBED: { AST_GET_CHILDREN(member, id, type, init); ast_t* p_id = ast_from_string(id, package_hygienic_id(t)); // The field is: var/let/embed id: type BUILD(field, member, NODE(ast_id(member), TREE(id) TREE(type) NONE NONE)); // The param is: $0: type BUILD(param, member, NODE(TK_PARAM, TREE(p_id) TREE(type) NONE)); // The arg is: $seq init BUILD(arg, init, NODE(TK_SEQ, TREE(init))); // The body of create contains: id = consume $0 BUILD_NO_DEBUG(assign, init, NODE(TK_ASSIGN, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) NODE(TK_REFERENCE, TREE(id)))); ast_setid(create_params, TK_PARAMS); ast_setid(call_args, TK_POSITIONALARGS); ast_append(class_members, field); ast_append(create_params, param); ast_append(create_body, assign); ast_append(call_args, arg); has_fields = true; break; } case TK_BE: // If we have behaviours, we must be an actor. ast_append(class_members, member); has_behaviours = true; break; default: // Keep all the methods as they are. ast_append(class_members, member); break; } member = ast_sibling(member); } if(!has_fields) { // End the constructor with 'true', since it has no parameters. BUILD_NO_DEBUG(true_node, ast, NODE(TK_TRUE)); ast_append(create_body, true_node); } // Handle capability and whether the anonymous type is a class, primitive or // actor. token_id cap_id = ast_id(cap); if(has_behaviours) { // Change the type to an actor. ast_setid(def, TK_ACTOR); if(cap_id != TK_NONE && cap_id != TK_TAG) { ast_error(cap, "object literals with behaviours are actors and so must " "have tag capability"); r = AST_ERROR; } } else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG || cap_id == TK_BOX || cap_id == TK_VAL)) { // Change the type from a class to a primitive. ast_setid(def, TK_PRIMITIVE); } else { // Type is a class, set the create capability as specified ast_setid(ast_child(create), cap_id); } // Add the create function at the end. ast_append(class_members, create); // Replace object..end with $0.create(...) ast_replace(astp, call); // Add new type to current module and bring it up to date with passes. ast_t* module = ast_nearest(ast, TK_MODULE); ast_append(module, def); if(!ast_passes_type(&def, opt)) return AST_FATAL; // Sugar the call. if(!ast_passes_subtree(astp, opt, PASS_SUGAR)) return AST_FATAL; return r; }
// Add all methods from the provides list of the given entity into lists in the // given symbol table. static bool collate_provided(ast_t* entity, methods_t* method_info) { assert(entity != NULL); bool r = true; ast_t* traits = ast_childidx(entity, 3); for(ast_t* t = ast_child(traits); t != NULL; t = ast_sibling(t)) { assert(ast_id(t) == TK_NOMINAL); ast_t* trait_def = (ast_t*)ast_data(t); assert(trait_def != NULL); // TODO: Check whether we need an error here if((ast_id(trait_def) != TK_TRAIT) && (ast_id(trait_def) != TK_INTERFACE)) return false; // Check for duplicates in our provides list // This is just simple compare of each entry against all the other. This is // clearly O(n^2), but since provides lists are likely to be small that // should be OK. If it turns out to be a problem it can be changed later. for(ast_t* p = ast_child(traits); p != t; p = ast_sibling(p)) { if(trait_def == (ast_t*)ast_data(p)) { ast_error(t, "duplicate entry in provides list"); ast_error(p, "previous entry here"); } } if(!build_entity_def(trait_def)) return false; ast_t* type_params = ast_childidx(trait_def, 1); ast_t* type_args = ast_childidx(t, 2); ast_t* trait_methods = ast_childidx(trait_def, 4); for(ast_t* m = ast_child(trait_methods); m != NULL; m = ast_sibling(m)) { // Reify the method with the type parameters from trait definition and // the reified type arguments from trait reference ast_t* r_method = reify(m, type_params, type_args); const char* entity_name = ast_name(ast_child(entity)); if(ast_id(r_method) == TK_BE || ast_id(r_method) == TK_NEW) { // Modify return type to the inheritting type ast_t* ret_type = ast_childidx(r_method, 4); assert(ast_id(ret_type) == TK_NOMINAL); const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE)); ast_set_name(ast_childidx(ret_type, 0), pkg_name); ast_set_name(ast_childidx(ret_type, 1), entity_name); } if(!add_method_to_list(r_method, method_info, entity_name)) r = false; } } return r; }
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; }
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; }
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; }
bool expr_object(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; bool ok = true; AST_GET_CHILDREN(ast, cap, provides, members); ast_clearflag(cap, AST_FLAG_PRESERVE); ast_clearflag(provides, AST_FLAG_PRESERVE); ast_clearflag(members, AST_FLAG_PRESERVE); ast_t* annotation = ast_consumeannotation(ast); const char* c_id = package_hygienic_id(&opt->check); ast_t* t_params; ast_t* t_args; collect_type_params(ast, &t_params, &t_args); const char* nice_id = (const char*)ast_data(ast); if(nice_id == NULL) nice_id = "object literal"; // Create a new anonymous type. BUILD(def, ast, NODE(TK_CLASS, AST_SCOPE ANNOTATE(annotation) NICE_ID(c_id, nice_id) TREE(t_params) NONE TREE(provides) NODE(TK_MEMBERS) NONE NONE)); // We will have a create method in the type. BUILD(create, members, NODE(TK_NEW, AST_SCOPE NONE ID("create") NONE NODE(TK_PARAMS) NONE NONE NODE(TK_SEQ, NODE(TK_TRUE)) NONE NONE)); BUILD(type_ref, ast, NODE(TK_REFERENCE, ID(c_id))); if(ast_id(t_args) != TK_NONE) { // Need to add type args to our type reference BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args))); type_ref = t; } ast_free_unattached(t_args); // We will replace object..end with $0.create(...) BUILD(call, ast, NODE(TK_CALL, NODE(TK_POSITIONALARGS) NONE NONE NODE(TK_DOT, TREE(type_ref) ID("create")))); ast_t* create_params = ast_childidx(create, 3); ast_t* create_body = ast_childidx(create, 6); ast_t* call_args = ast_child(call); ast_t* class_members = ast_childidx(def, 4); ast_t* member = ast_child(members); bool has_fields = false; bool has_behaviours = false; while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: case TK_EMBED: { add_field_to_object(opt, member, class_members, create_params, create_body, call_args); has_fields = true; break; } case TK_BE: // If we have behaviours, we must be an actor. ast_append(class_members, member); has_behaviours = true; break; default: // Keep all the methods as they are. ast_append(class_members, member); break; } member = ast_sibling(member); } // Add the create function at the end. ast_append(class_members, create); // Add new type to current module and bring it up to date with passes. ast_t* module = ast_nearest(ast, TK_MODULE); ast_append(module, def); // Turn any free variables into fields. ast_t* captures = ast_from(ast, TK_MEMBERS); ast_t* last_capture = NULL; if(!capture_from_type(opt, *astp, &def, captures, &last_capture)) ok = false; for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { add_field_to_object(opt, p, class_members, create_params, create_body, call_args); has_fields = true; } ast_free_unattached(captures); ast_resetpass(def, PASS_SUGAR); // Handle capability and whether the anonymous type is a class, primitive or // actor. token_id cap_id = ast_id(cap); if(has_behaviours) { // Change the type to an actor. ast_setid(def, TK_ACTOR); if(cap_id != TK_NONE && cap_id != TK_TAG) { ast_error(opt->check.errors, cap, "object literals with behaviours are " "actors and so must have tag capability"); ok = false; } cap_id = TK_TAG; } else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG || cap_id == TK_BOX || cap_id == TK_VAL)) { // Change the type from a class to a primitive. ast_setid(def, TK_PRIMITIVE); cap_id = TK_VAL; } if(ast_id(def) != TK_PRIMITIVE) pony_assert(!ast_has_annotation(def, "ponyint_bare")); // Reset constructor to pick up the correct defaults. ast_setid(ast_child(create), cap_id); ast_t* result = ast_childidx(create, 4); ast_replace(&result, type_for_class(opt, def, result, cap_id, TK_EPHEMERAL, false)); // Catch up provides before catching up the entire type. if(!catch_up_provides(opt, provides)) return false; // Type check the anonymous type. if(!ast_passes_type(&def, opt, PASS_EXPR)) return false; // Replace object..end with $0.create(...) ast_replace(astp, call); if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; if(!ast_passes_subtree(astp, opt, PASS_EXPR)) return false; return ok; }
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; }
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; }
// Sugar for partial application, which we convert to a lambda. static bool partial_application(pass_opt_t* opt, ast_t** astp) { /* Example that we refer to throughout this function. * ```pony * class C * fun f[T](a: A, b: B = b_default): R * * let recv: T = ... * recv~f[T2](foo) * ``` * * Partial call is converted to: * ```pony * {(b: B = b_default)($0 = recv, a = foo): R => $0.f[T2](a, consume b) } * ``` */ ast_t* ast = *astp; typecheck_t* t = &opt->check; if(!method_application(opt, ast, true)) return false; AST_GET_CHILDREN(ast, positional, namedargs, question, lhs); // LHS must be an application, possibly wrapped in another application // if the method had type parameters for qualification. pony_assert(ast_id(lhs) == TK_FUNAPP || ast_id(lhs) == TK_BEAPP || ast_id(lhs) == TK_NEWAPP); AST_GET_CHILDREN(lhs, receiver, method); ast_t* type_args = NULL; if(ast_id(receiver) == ast_id(lhs)) { type_args = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); } // Look up the original method definition for this method call. ast_t* method_def = lookup(opt, lhs, ast_type(receiver), ast_name(method)); pony_assert(ast_id(method_def) == TK_FUN || ast_id(method_def) == TK_BE || ast_id(method_def) == TK_NEW); // The TK_FUNTYPE of the LHS. ast_t* type = ast_type(lhs); pony_assert(ast_id(type) == TK_FUNTYPE); if(is_typecheck_error(type)) return false; AST_GET_CHILDREN(type, cap, type_params, target_params, result); bool bare = ast_id(cap) == TK_AT; token_id apply_cap = TK_AT; if(!bare) apply_cap = partial_application_cap(opt, type, receiver, positional); token_id can_error = ast_id(ast_childidx(method_def, 5)); const char* recv_name = package_hygienic_id(t); // Build lambda expression. ast_t* call_receiver = NULL; if(bare) { ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) != TK_NONE) { ast_error(opt->check.errors, arg, "the partial application of a bare " "method cannot take arguments"); return false; } arg = ast_sibling(arg); } ast_t* receiver_type = ast_type(receiver); if(is_bare(receiver_type)) { // Partial application on a bare object, simply return the object itself. ast_replace(astp, receiver); return true; } AST_GET_CHILDREN(receiver_type, recv_type_package, recv_type_name); const char* recv_package_str = ast_name(recv_type_package); const char* recv_name_str = ast_name(recv_type_name); ast_t* module = ast_nearest(ast, TK_MODULE); ast_t* package = ast_parent(module); ast_t* pkg_id = package_id(package); const char* pkg_str = ast_name(pkg_id); const char* pkg_alias = NULL; if(recv_package_str != pkg_str) pkg_alias = package_alias_from_id(module, recv_package_str); ast_free_unattached(pkg_id); if(pkg_alias != NULL) { // `package.Type.f` BUILD_NO_DECL(call_receiver, ast, NODE(TK_DOT, NODE(TK_DOT, NODE(TK_REFERENCE, ID(pkg_alias)) ID(recv_name_str)) TREE(method))); } else { // `Type.f` BUILD_NO_DECL(call_receiver, ast, NODE(TK_DOT, NODE(TK_REFERENCE, ID(recv_name_str)) TREE(method))); } } else { // `$0.f` BUILD_NO_DECL(call_receiver, ast, NODE(TK_DOT, NODE(TK_REFERENCE, ID(recv_name)) TREE(method))); } ast_t* captures = NULL; if(bare) { captures = ast_from(receiver, TK_NONE); } else { // Build captures. We always have at least one capture, for receiver. // Capture: `$0 = recv` BUILD_NO_DECL(captures, receiver, NODE(TK_LAMBDACAPTURES, NODE(TK_LAMBDACAPTURE, ID(recv_name) NONE // Infer type. TREE(receiver)))); } // Process arguments. ast_t* target_param = ast_child(target_params); ast_t* lambda_params = ast_from(target_params, TK_NONE); ast_t* lambda_call_args = ast_from(positional, TK_NONE); ast_t* given_arg = ast_child(positional); while(given_arg != NULL) { pony_assert(target_param != NULL); const char* target_p_name = ast_name(ast_child(target_param)); if(ast_id(given_arg) == TK_NONE) { // This argument is not supplied already, must be a lambda parameter. // Like `b` in example above. // Build a new a new TK_PARAM node rather than copying the target one, // since the target has already been processed to expr pass, and we need // a clean one. AST_GET_CHILDREN(target_param, p_id, p_type, p_default); // Parameter: `b: B = b_default` BUILD(lambda_param, target_param, NODE(TK_PARAM, TREE(p_id) TREE(sanitise_type(p_type)) TREE(p_default))); ast_append(lambda_params, lambda_param); ast_setid(lambda_params, TK_PARAMS); // Argument: `consume b` BUILD(target_arg, lambda_param, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(target_p_name))))); ast_append(lambda_call_args, target_arg); ast_setid(lambda_call_args, TK_POSITIONALARGS); } else { // This argument is supplied to the partial, capture it. // Like `a` in example above. // Capture: `a = foo` BUILD(capture, given_arg, NODE(TK_LAMBDACAPTURE, ID(target_p_name) NONE TREE(given_arg))); ast_append(captures, capture); // Argument: `a` BUILD(target_arg, given_arg, NODE(TK_SEQ, NODE(TK_REFERENCE, ID(target_p_name)))); ast_append(lambda_call_args, target_arg); ast_setid(lambda_call_args, TK_POSITIONALARGS); } given_arg = ast_sibling(given_arg); target_param = ast_sibling(target_param); } pony_assert(target_param == NULL); if(type_args != NULL) { // The partial call has type args, add them to the actual call in apply(). // `$0.f[T2]` BUILD(qualified, type_args, NODE(TK_QUALIFY, TREE(call_receiver) TREE(type_args))); call_receiver = qualified; } REPLACE(astp, NODE((bare ? TK_BARELAMBDA : TK_LAMBDA), NODE(apply_cap) NONE // Lambda function name. NONE // Lambda type params. TREE(lambda_params) TREE(captures) TREE(sanitise_type(result)) NODE(can_error) NODE(TK_SEQ, NODE(TK_CALL, TREE(lambda_call_args) NONE // Named args. NODE(can_error) TREE(call_receiver))) NONE)); // Lambda reference capability. // Need to preserve various lambda children. ast_setflag(ast_childidx(*astp, 2), AST_FLAG_PRESERVE); // Type params. ast_setflag(ast_childidx(*astp, 3), AST_FLAG_PRESERVE); // Parameters. ast_setflag(ast_childidx(*astp, 5), AST_FLAG_PRESERVE); // Return type. ast_setflag(ast_childidx(*astp, 7), AST_FLAG_PRESERVE); // Body. // Catch up to this pass. return ast_passes_subtree(astp, opt, PASS_EXPR); }
bool expr_assign(pass_opt_t* opt, ast_t* ast) { // Left and right are swapped in the AST to make sure we type check the // right side before the left. Fetch them in the opposite order. assert(ast != NULL); AST_GET_CHILDREN(ast, right, left); ast_t* l_type = ast_type(left); if(l_type == NULL || !is_lvalue(opt, left, is_result_needed(ast))) { ast_error(opt->check.errors, ast, "left side must be something that can be assigned to"); return false; } if(!coerce_literals(&right, l_type, opt)) return false; ast_t* r_type = ast_type(right); if(is_typecheck_error(r_type)) return false; if(is_control_type(r_type)) { ast_error(opt->check.errors, ast, "the right hand side does not return a value"); return false; } if(!infer_locals(opt, left, r_type)) return false; // Inferring locals may have changed the left type. l_type = ast_type(left); // Assignment is based on the alias of the right hand side. ast_t* a_type = alias(r_type); errorframe_t info = NULL; if(!is_subtype(a_type, l_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "right side must be a subtype of left side"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ast_free_unattached(a_type); return false; } if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE)) { switch(ast_id(a_type)) { case TK_UNIONTYPE: ast_error(opt->check.errors, ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(opt->check.errors, ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } ast_free_unattached(a_type); return false; } bool ok_safe = safe_to_write(left, a_type); if(!ok_safe) { if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL && ast_id(ast_child(left)) == TK_THIS) { // We are writing to a field in this ast_t* fn = ast_nearest(left, TK_FUN); if(fn != NULL) { ast_t* iso = ast_child(fn); assert(iso != NULL); token_id iso_id = ast_id(iso); if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG) { ast_error(opt->check.errors, ast, "cannot write to a field in a %s function. If you are trying to " "change state in a function use fun ref", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(opt->check.errors, ast, "not safe to write right side to left side"); ast_error_continue(opt->check.errors, a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); if(!check_embed_construction(opt, left, right)) return false; ast_settype(ast, consume_type(l_type, TK_NONE)); return true; }
// Attempt to find the specified package directory in our search path // @return The resulting directory path, which should not be deleted and is // valid indefinitely. NULL is directory cannot be found. static const char* find_path(ast_t* from, const char* path, bool* out_is_relative) { if(out_is_relative != NULL) *out_is_relative = false; // First check for an absolute path if(is_path_absolute(path)) return try_path(NULL, path); // Get the base directory const char* base; if((from == NULL) || (ast_id(from) == TK_PROGRAM)) { base = NULL; } else { from = ast_nearest(from, TK_PACKAGE); package_t* pkg = (package_t*)ast_data(from); base = pkg->path; } // Try a path relative to the base const char* result = try_path(base, path); if(result != NULL) { if(out_is_relative != NULL) *out_is_relative = true; return result; } // If it's a relative path, don't try elsewhere if(!is_path_relative(path)) { // Check ../pony_packages and further up the tree if(base != NULL) { result = try_package_path(base, path); if(result != NULL) return result; // Check ../pony_packages from the compiler target if((from != NULL) && (ast_id(from) == TK_PACKAGE)) { ast_t* target = ast_child(ast_parent(from)); package_t* pkg = (package_t*)ast_data(target); base = pkg->path; result = try_package_path(base, path); if(result != NULL) return result; } } // Try the search paths for(strlist_t* p = search; p != NULL; p = strlist_next(p)) { result = try_path(strlist_data(p), path); if(result != NULL) return result; } } errorf(path, "couldn't locate this path"); return NULL; }
bool expr_assign(pass_opt_t* opt, ast_t* ast) { // Left and right are swapped in the AST to make sure we type check the // right side before the left. Fetch them in the opposite order. assert(ast != NULL); AST_GET_CHILDREN(ast, right, left); ast_t* l_type = ast_type(left); if(!is_lvalue(&opt->check, left, is_result_needed(ast))) { ast_error(ast, "left side must be something that can be assigned to"); return false; } assert(l_type != NULL); if(!coerce_literals(&right, l_type, opt)) return false; ast_t* r_type = ast_type(right); if(is_typecheck_error(r_type)) return false; if(!infer_locals(left, r_type)) return false; // Inferring locals may have changed the left type. l_type = ast_type(left); // Assignment is based on the alias of the right hand side. ast_t* a_type = alias(r_type); if(!is_subtype(a_type, l_type)) { ast_error(ast, "right side must be a subtype of left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_error(l_type, "left side type: %s", ast_print_type(l_type)); ast_free_unattached(a_type); return false; } if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE)) { switch(ast_id(a_type)) { case TK_UNIONTYPE: ast_error(ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } bool ok_safe = safe_to_write(left, a_type); if(!ok_safe) { if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL && ast_id(ast_child(left)) == TK_THIS) { // We are writing to a field in this ast_t* fn = ast_nearest(left, TK_FUN); if(fn != NULL) { ast_t* iso = ast_child(fn); assert(iso != NULL); token_id iso_id = ast_id(iso); if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG) { ast_error(ast, "cannot write to a field in a %s function", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(ast, "not safe to write right side to left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); // If it's an embedded field, check for a constructor result. if(ast_id(left) == TK_EMBEDREF) { if((ast_id(right) != TK_CALL) || (ast_id(ast_childidx(right, 2)) != TK_NEWREF)) { ast_error(ast, "an embedded field must be assigned using a constructor"); return false; } } ast_settype(ast, consume_type(l_type, TK_NONE)); ast_inheritflags(ast); return true; }
const char* package_name(ast_t* ast) { package_t* pkg = (package_t*)ast_data(ast_nearest(ast, TK_PACKAGE)); return (pkg == NULL) ? NULL : pkg->id; }
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; }