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; }
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; }
// 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; }