static ast_t* viewpoint_for_type(token_id view, token_id eph, ast_t* type, int cap_index) { ast_t* cap = ast_childidx(type, cap_index); token_id tcap = ast_id(cap); token_id rcap = cap_viewpoint(view, tcap); if(rcap == TK_NONE) return NULL; if((tcap != rcap) || (eph == TK_EPHEMERAL)) { type = ast_dup(type); cap = ast_childidx(type, cap_index); ast_setid(cap, rcap); if(eph == TK_EPHEMERAL && ((view == TK_ISO) || (view == TK_TRN))) { // If we're adapting from an ephemeral type, make this type ephemeral. // iso^->iso = iso^, previous views were iso, alias(iso^) ~ alias(iso) // iso^->trn = trn^, previous views were tag, alias(trn^) ~ alias(tag) // trn^->iso = iso^, previous views were iso, alias(iso^) ~ alias(iso) // trn^->trn = trn^, previous views were trn, alias(trn^) ~ alias(trn) // alias(iso)->x isn't alllowed // alias(trn)->x = box->x // alias(iso^) ~ alias(box->iso) // alias(trn^) ~ alias(box->trn) // Doesn't work for ref, isn't needed for val, box or tag. ast_t* ephemeral = ast_sibling(cap); ast_setid(ephemeral, eph); } } return type; }
ast_t* set_cap_and_ephemeral(ast_t* type, token_id cap, token_id ephemeral) { switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { ast_t* child = ast_child(type); type = ast_from(type, ast_id(type)); while(child != NULL) { ast_append(type, set_cap_and_ephemeral(child, cap, ephemeral)); child = ast_sibling(child); } return type; } case TK_NOMINAL: { type = ast_dup(type); AST_GET_CHILDREN(type, package, id, typeargs, tcap, eph); if(cap != TK_NONE) ast_setid(tcap, cap); ast_setid(eph, ephemeral); return type; } case TK_TYPEPARAMREF: { type = ast_dup(type); AST_GET_CHILDREN(type, id, tcap, eph); if(cap != TK_NONE) ast_setid(tcap, cap); ast_setid(eph, ephemeral); return type; } case TK_ARROW: // Just use the lhs of the viewpoint type. return set_cap_and_ephemeral(ast_childidx(type, 1), cap, ephemeral); default: {} } assert(0); return NULL; }
bool expr_tilde(pass_opt_t* opt, ast_t** astp) { if(!dot_or_tilde(opt, astp, true)) return false; ast_t* ast = *astp; if(ast_id(ast) == TK_TILDE && ast_type(ast) != NULL && ast_id(ast_type(ast)) == TK_OPERATORLITERAL) { ast_error(opt->check.errors, ast, "can't do partial application on a literal number"); return false; } switch(ast_id(ast)) { case TK_NEWREF: case TK_NEWBEREF: ast_setid(ast, TK_NEWAPP); return true; case TK_BEREF: ast_setid(ast, TK_BEAPP); return true; case TK_FUNREF: ast_setid(ast, TK_FUNAPP); return true; case TK_TYPEREF: ast_error(opt->check.errors, ast, "can't do partial application on a package"); return false; case TK_FVARREF: case TK_FLETREF: case TK_EMBEDREF: ast_error(opt->check.errors, ast, "can't do partial application of a field"); return false; default: {} } assert(0); return false; }
static ast_result_t sugar_new(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; AST_GET_CHILDREN(ast, cap, id, typeparams, params, result); // Return type default to ref^ for classes, val^ for primitives, and // tag^ for actors. if(ast_id(result) == TK_NONE) { token_id tcap = ast_id(cap); if(tcap == TK_NONE) { switch(ast_id(t->frame->type)) { case TK_PRIMITIVE: tcap = TK_VAL; break; case TK_ACTOR: tcap = TK_TAG; break; default: tcap = TK_REF; break; } ast_setid(cap, tcap); } ast_replace(&result, type_for_this(opt, ast, tcap, TK_EPHEMERAL, false)); } sugar_docstring(ast); return check_method(ast); }
static bool tuple_access(ast_t* ast) { // Left is a postfix expression, right is a lookup name. ast_t* left = ast_child(ast); ast_t* right = ast_sibling(left); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; // Change the lookup name to an integer index. if(!make_tuple_index(&right)) { ast_error(right, "lookup on a tuple must take the form _X, where X is an integer"); return false; } // Make sure our index is in bounds. type = ast_childidx(type, (size_t)ast_int(right)); if(type == NULL) { ast_error(right, "tuple index is out of bounds"); return false; } ast_setid(ast, TK_FLETREF); ast_settype(ast, type); ast_inheritflags(ast); return true; }
void fun_defaults(ast_t* ast) { assert(ast != NULL); AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body, docstring); // If the receiver cap is not specified, set it to box. if(ast_id(cap) == TK_NONE) ast_setid(cap, TK_BOX); // If the return value is not specified, set it to None. if(ast_id(result) == TK_NONE) { ast_t* type = type_sugar(ast, NULL, "None"); ast_replace(&result, type); } // If the return type is None, add a None at the end of the body, unless it // already ends with an error or return statement if(is_none(result) && (ast_id(body) != TK_NONE)) { ast_t* last_cmd = ast_childlast(body); if(ast_id(last_cmd) != TK_ERROR && ast_id(last_cmd) != TK_RETURN) { BUILD_NO_DEBUG(ref, body, NODE(TK_REFERENCE, ID("None"))); ast_append(body, ref); } } }
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg) { // Pick up a default argument. ast_t* def_arg = ast_childidx(param, 2); if(ast_id(def_arg) == TK_NONE) { ast_error(arg, "not enough arguments"); return false; } ast_setid(arg, TK_SEQ); ast_add(arg, def_arg); // Type check the arg. if(ast_type(def_arg) == NULL) { if(ast_visit(&arg, NULL, pass_expr, opt) != AST_OK) return false; ast_visit(&arg, NULL, pass_nodebug, opt); } else { if(!expr_seq(arg)) return false; } return true; }
static bool names_type(typecheck_t* t, ast_t** astp, ast_t* def) { ast_t* ast = *astp; AST_GET_CHILDREN(ast, package, id, typeparams, cap, eph); token_id tcap = ast_id(cap); if((tcap == TK_NONE) && (ast_id(def) == TK_PRIMITIVE)) { // A primitive without a capability is a val, even if it is a constraint. tcap = TK_VAL; } else if(t->frame->constraint != NULL) { // A constraint is modified to a generic capability. switch(tcap) { case TK_NONE: tcap = TK_ANY_GENERIC; break; case TK_BOX: tcap = TK_BOX_GENERIC; break; case TK_TAG: tcap = TK_TAG_GENERIC; break; default: {} } } else if(tcap == TK_NONE) { // Otherwise, we use the default capability. tcap = ast_id(ast_childidx(def, 2)); } ast_setid(cap, tcap); // Keep the actual package id. ast_append(ast, package); ast_replace(&package, package_id(def)); // Store our definition for later use. ast_setdata(ast, def); return true; }
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg) { // Pick up a default argument. AST_GET_CHILDREN(param, id, type, def_arg); if(ast_id(def_arg) == TK_NONE) { ast_error(opt->check.errors, arg, "not enough arguments"); return false; } if(ast_id(def_arg) == TK_LOCATION) { // Default argument is __loc. Expand call location. ast_t* location = expand_location(arg); ast_add(arg, location); if(!ast_passes_subtree(&location, opt, PASS_EXPR)) return false; } else { // Just use default argument. ast_add(arg, def_arg); } ast_setid(arg, TK_SEQ); if(!expr_seq(opt, arg)) return false; return true; }
static ast_result_t sugar_update(ast_t** astp) { ast_t* ast = *astp; assert(ast_id(ast) == TK_ASSIGN); AST_GET_CHILDREN(ast, value, call); if(ast_id(call) != TK_CALL) return AST_OK; // We are of the form: x(y) = z // Replace us with: x.update(y where value = z) AST_EXTRACT_CHILDREN(call, positional, named, expr); // If there are no named arguments yet, named will be a TK_NONE. ast_setid(named, TK_NAMEDARGS); // Build a new namedarg. BUILD_NO_DEBUG(namedarg, ast, NODE(TK_UPDATEARG, ID("value") NODE(TK_SEQ, TREE(value)))); // Append the named arg to our existing list. ast_append(named, namedarg); // Replace with the update call. REPLACE(astp, NODE(TK_CALL, TREE(positional) TREE(named) NODE(TK_DOT, TREE(expr) ID("update")))); return AST_OK; }
/* Load an ID node. * IDs are indicated by the keyword id followed by the ID name, all contained * within parentheses. For example: * (id foo) * * The ( and id keyword must have been parsed before this is called. */ static ast_t* get_id(build_parser_t* builder, ast_t* existing_ast) { assert(builder != NULL); if(existing_ast != NULL) { ast_free(existing_ast); build_error(builder, "Seen ID not first in node"); return NULL; } ast_token_id id = get_token(builder); if(id != AT_ID && id != AT_STRING) { build_error(builder, "ID name expected"); return NULL; } ast_t* ast = ast_token(builder->token); ast_setid(ast, TK_ID); save_token(builder); if(get_token(builder) != AT_RPAREN) { build_error(builder, "Close paren expected for ID"); ast_free(ast); return NULL; } return ast; }
// 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 bool apply_named_args(pass_opt_t* opt, ast_t* params, ast_t* positional, ast_t* namedargs) { ast_t* namedarg = ast_pop(namedargs); while(namedarg != NULL) { AST_GET_CHILDREN(namedarg, arg_id, arg); ast_t* param = ast_child(params); size_t param_index = 0; while(param != NULL) { AST_GET_CHILDREN(param, param_id); if(ast_name(arg_id) == ast_name(param_id)) break; param = ast_sibling(param); param_index++; } if(param == NULL) { if(ast_id(namedarg) == TK_UPDATEARG) { ast_error(opt->check.errors, arg_id, "cannot use sugar, update() has no parameter named \"value\""); return false; } ast_error(opt->check.errors, arg_id, "not a parameter name"); return false; } ast_t* arg_replace = ast_childidx(positional, param_index); if(ast_id(arg_replace) != TK_NONE) { ast_error(opt->check.errors, arg_id, "named argument is already supplied"); ast_error_continue(opt->check.errors, arg_replace, "supplied argument is here"); return false; } // Extract named argument expression to avoid copying it ast_free(ast_pop(namedarg)); // ID arg = ast_pop(namedarg); // Expression ast_replace(&arg_replace, arg); namedarg = ast_pop(namedargs); } ast_setid(namedargs, TK_NONE); return true; }
bool reify_defaults(ast_t* typeparams, ast_t* typeargs, bool errors, pass_opt_t* opt) { assert( (ast_id(typeparams) == TK_TYPEPARAMS) || (ast_id(typeparams) == TK_NONE) ); assert( (ast_id(typeargs) == TK_TYPEARGS) || (ast_id(typeargs) == TK_NONE) ); size_t param_count = ast_childcount(typeparams); size_t arg_count = ast_childcount(typeargs); if(param_count == arg_count) return true; if(param_count < arg_count) { if(errors) { ast_error(opt->check.errors, typeargs, "too many type arguments"); ast_error_continue(opt->check.errors, typeparams, "definition is here"); } return false; } // Pick up default type arguments if they exist. ast_setid(typeargs, TK_TYPEARGS); ast_t* typeparam = ast_childidx(typeparams, arg_count); while(typeparam != NULL) { ast_t* defarg = ast_childidx(typeparam, 2); if(ast_id(defarg) == TK_NONE) break; ast_append(typeargs, defarg); typeparam = ast_sibling(typeparam); } if(typeparam != NULL) { if(errors) { ast_error(opt->check.errors, typeargs, "not enough type arguments"); ast_error_continue(opt->check.errors, typeparams, "definition is here"); } return false; } 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(pass_opt_t* opt, 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); AST_GET_CHILDREN(reified_method, cap, id, typeparams, params, result, can_error, body, doc); if(ast_id(reified_method) == TK_BE || ast_id(reified_method) == TK_NEW) { // Modify return type to the inheriting type ast_t* this_type = type_for_this(opt, entity, ast_id(cap), TK_EPHEMERAL, true); ast_replace(&result, this_type); } // Ignore docstring 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(id); 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; }
// Determine the UIF types that satisfy the given "simple" type. // Here a simple type is defined as a non-tuple type that does not depend on // any formal parameters. static int uifset_simple_type(pass_opt_t* opt, ast_t* type) { assert(type != NULL); int set = 0; for(int i = 0; i < UIF_COUNT; i++) { ast_t* uif = type_builtin(opt, type, _str_uif_types[i].name); ast_setid(ast_childidx(uif, 3), TK_VAL); ast_setid(ast_childidx(uif, 4), TK_EPHEMERAL); if(is_subtype(uif, type)) set |= (1 << i); ast_free(uif); } return set; }
bool expr_chain(pass_opt_t* opt, ast_t** astp) { if(!entity_access(opt, astp)) return false; ast_t* ast = *astp; switch(ast_id(ast)) { case TK_BEREF: ast_setid(ast, TK_BECHAIN); return true; case TK_FUNREF: ast_setid(ast, TK_FUNCHAIN); return true; case TK_NEWREF: case TK_NEWBEREF: ast_error(opt->check.errors, ast, "can't do method chaining on a constructor"); return false; case TK_TYPEREF: ast_error(opt->check.errors, ast, "can't do method chaining on a package"); return false; case TK_FVARREF: case TK_FLETREF: case TK_EMBEDREF: ast_error(opt->check.errors, ast, "can't do method chaining on a field"); return false; default: {} } assert(0); return false; }
// Convert the given method into a delegation indirection to the specified // field. static void make_delegation(ast_t* method, ast_t* field, ast_t* delegate_ref, ast_t* body_donor) { assert(method != NULL); assert(field != NULL); assert(delegate_ref != NULL); // Make a redirection method body. ast_t* args = ast_from(delegate_ref, TK_NONE); ast_t* last_arg = NULL; AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body); for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { const char* param_name = ast_name(ast_child(p)); BUILD(arg, delegate_ref, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(param_name))))); ast_list_append(args, &last_arg, arg); ast_setid(args, TK_POSITIONALARGS); } BUILD(body, delegate_ref, NODE(TK_SEQ, NODE(TK_CALL, TREE(args) // Positional args. NODE(TK_NONE) // Named args. NODE(TK_DOT, // Receiver. NODE(TK_REFERENCE, ID(ast_name(ast_child(field)))) ID(ast_name(ast_childidx(method, 1))))))); if(is_none(result)) { // Add None to end of body. Whilst the call generated above will return // None anyway in this case, without this extra None testing is very hard // since a directly written version of this body will have the None. BUILD(none, delegate_ref, NODE(TK_REFERENCE, ID("None"))); ast_append(body, none); } ast_replace(&old_body, body); // Setup method info. method_t* info = (method_t*)ast_data(method); assert(info != NULL); info->body_donor = body_donor; info->delegated_field = field; }
// If the given tree is a TK_NONE expand it to a source None static void expand_none(ast_t* ast, bool is_scope) { if(ast_id(ast) != TK_NONE) return; if(is_scope) ast_scope(ast); ast_setid(ast, TK_SEQ); BUILD_NO_DEBUG(ref, ast, NODE(TK_REFERENCE, ID("None"))); ast_add(ast, ref); }
static bool method_access(pass_opt_t* opt, ast_t* ast, ast_t* method) { AST_GET_CHILDREN(method, cap, id, typeparams, params, result, can_error, body); switch(ast_id(method)) { case TK_NEW: { AST_GET_CHILDREN(ast, left, right); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; if(!constructor_type(opt, ast, ast_id(cap), type, &result)) return false; break; } case TK_BE: ast_setid(ast, TK_BEREF); break; case TK_FUN: ast_setid(ast, TK_FUNREF); break; default: assert(0); return false; } ast_settype(ast, type_for_fun(method)); if(ast_id(can_error) == TK_QUESTION) ast_seterror(ast); return is_method_called(opt, ast); }
static ast_result_t sugar_try(ast_t* ast) { AST_GET_CHILDREN(ast, ignore, else_clause, then_clause); if(ast_id(else_clause) == TK_NONE && ast_id(then_clause) != TK_NONE) // Then without else means we don't require a throwable in the try block ast_setid(ast, TK_TRY_NO_CHECK); expand_none(else_clause, true); expand_none(then_clause, true); return AST_OK; }
// Collect the given type parameter static void collect_type_param(ast_t* orig_param, ast_t* params, ast_t* args) { assert(orig_param != NULL); // Get original type parameter info AST_GET_CHILDREN(orig_param, id, constraint, deflt); const char* name = ast_name(id); constraint = sanitise_type(constraint); assert(constraint != NULL); // New type parameter has the same constraint as the old one (sanitised) if(params != NULL) { BUILD(new_param, orig_param, NODE(TK_TYPEPARAM, ID(name) TREE(constraint) NONE)); ast_append(params, new_param); ast_setid(params, TK_TYPEPARAMS); } // New type arguments binds to old type parameter if(args != NULL) { BUILD(new_arg, orig_param, NODE(TK_NOMINAL, NONE // Package ID(name) NONE // Type args NONE // cap NONE)); // ephemeral ast_append(args, new_arg); ast_setid(args, TK_TYPEARGS); } }
static ast_result_t sugar_let(typecheck_t* t, ast_t* ast) { ast_t* id = ast_child(ast); if(ast_id(id) == TK_DONTCARE) { // Replace "_" with "$1" in with and for variable lists ast_setid(id, TK_ID); ast_set_name(id, package_hygienic_id(t)); } return AST_OK; }
static ast_result_t sugar_be(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body); ast_setid(cap, TK_TAG); if(ast_id(result) == TK_NONE) { // Return type is This tag ast_replace(&result, type_for_this(opt, ast, TK_TAG, TK_NONE, false)); } sugar_docstring(ast); return check_method(ast); }
static ast_t* viewpoint_lower_for_type(ast_t* type, int cap_index) { ast_t* cap = ast_childidx(type, cap_index); token_id tcap = ast_id(cap); // For any chain of arrows, return a capability that is a subtype of the // resulting capability. // ref->iso = iso, val->iso = val, box->iso = tag => iso // ref->trn = trn, val->trn = val, box->trn = box => trn // ref->ref = ref, val->ref = val, box->ref = box => trn // ref->val = val, val->val = val, box->val = val => val // ref->box = box, val->box = val, box->box = box => val // ref->tag = tag, val->tag = tag, box->tag = tag => tag // #read: ref = trn, val = val, box = val => trn // #send: iso = iso, val = val, tag = tag => iso // #share: val = val, tag = tag => val // #any: iso = iso => iso switch(tcap) { case TK_ISO: case TK_TRN: case TK_VAL: case TK_TAG: return type; case TK_REF: case TK_CAP_READ: tcap = TK_TRN; break; case TK_BOX: case TK_CAP_SHARE: tcap = TK_VAL; break; case TK_CAP_SEND: case TK_CAP_ANY: tcap = TK_ISO; break; default: assert(0); return NULL; } type = ast_dup(type); cap = ast_childidx(type, cap_index); ast_setid(cap, tcap); return type; }
static ast_result_t sugar_return(typecheck_t* t, ast_t* ast) { ast_t* return_value = ast_child(ast); if((ast_id(ast) == TK_RETURN) && (ast_id(t->frame->method) == TK_NEW)) { assert(ast_id(return_value) == TK_NONE); ast_setid(return_value, TK_THIS); } else { expand_none(return_value, false); } return AST_OK; }
// Collect the given type parameter static void collect_type_param(ast_t* orig_param, ast_t* params, ast_t* args) { assert(orig_param != NULL); assert(params != NULL); assert(args != NULL); // Get original type parameter info AST_GET_CHILDREN(orig_param, id, constraint, deflt); const char* name = ast_name(id); constraint = sanitise_type(constraint); assert(constraint != NULL); // New type parameter has the same constraint as the old one (sanitised) BUILD(new_param, orig_param, NODE(TK_TYPEPARAM, ID(name) TREE(constraint) NONE)); ast_append(params, new_param); // New type arguments binds to old type parameter BUILD(new_arg, orig_param, NODE(TK_TYPEPARAMREF, DATA(orig_param) ID(name) NONE // cap NONE)); // ephemeral ast_append(args, new_arg); // Since we have a type parameter the params and args node should not be // TK_NONE ast_setid(params, TK_TYPEPARAMS); ast_setid(args, TK_TYPEARGS); }
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(ast, "can't read a field through %s", ast_print_type(l_type)); return false; } ast_free_unattached(upper); } // 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'. ast_t* id = ast_child(find); 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; }
// Check type parameters and build the all type parameter structures for a // complete set of case methods with the given name. // Generate type parameter list for worker call. static void build_t_params(ast_t* match_t_params, ast_t* worker_t_params) { assert(match_t_params != NULL); assert(worker_t_params != NULL); for(ast_t* p = ast_child(match_t_params); p != NULL; p = ast_sibling(p)) { AST_GET_CHILDREN(p, id, constraint, def_type); assert(ast_id(id) == TK_ID); // Add type parameter name to worker call list. BUILD(type, p, NODE(TK_NOMINAL, NONE TREE(id) NONE NONE NONE)); ast_append(worker_t_params, type); ast_setid(worker_t_params, TK_TYPEARGS); } }
ast_t* type_for_this(typecheck_t* t, ast_t* ast, token_id cap, token_id ephemeral) { bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } AST_GET_CHILDREN(t->frame->type, id, typeparams); BUILD(typeargs, ast, NODE(TK_NONE)); BUILD(type, ast, NODE(TK_NOMINAL, NODE(TK_NONE) TREE(id) TREE(typeargs) NODE(cap) NODE(ephemeral))); if(ast_id(typeparams) == TK_TYPEPARAMS) { ast_setid(typeargs, TK_TYPEARGS); ast_t* typeparam = ast_child(typeparams); while(typeparam != NULL) { ast_t* typeparam_id = ast_child(typeparam); ast_t* typearg = type_sugar(ast, NULL, ast_name(typeparam_id)); ast_append(typeargs, typearg); typeparam = ast_sibling(typeparam); } } if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); return arrow; } return type; }