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; } }
static bool check_nonsendable_recover(pass_opt_t* opt, ast_t* ast) { if(opt->check.frame->recover != NULL) { AST_GET_CHILDREN(ast, positional, namedargs, question, lhs); ast_t* type = ast_type(lhs); AST_GET_CHILDREN(type, cap, typeparams, params, result); // If the method is tag, the call is always safe. if(ast_id(cap) == TK_TAG) return true; ast_t* receiver = ast_child(lhs); // Dig through function qualification. if((ast_id(receiver) == TK_FUNREF) || (ast_id(receiver) == TK_FUNAPP) || (ast_id(receiver) == TK_FUNCHAIN)) receiver = ast_child(receiver); if(!is_receiver_safe(&opt->check, receiver)) { ast_t* arg = ast_child(positional); bool args_sendable = true; while(arg != NULL) { if(ast_id(arg) != TK_NONE) { // Don't typecheck arg_type, this was already done in // auto_recover_call. ast_t* arg_type = ast_type(arg); if(!sendable(arg_type)) { args_sendable = false; break; } } arg = ast_sibling(arg); } if(!args_sendable || !sendable(result)) { ast_error(opt->check.errors, ast, "can't call method on non-sendable " "object inside of a recover expression"); ast_error_continue(opt->check.errors, ast, "this would be possible if " "the arguments and return value were all sendable"); return false; } } } return true; }
static ast_result_t flatten_sendable_params(ast_t* params) { ast_t* param = ast_child(params); ast_result_t r = AST_OK; while(param != NULL) { AST_GET_CHILDREN(param, id, type, def); if(!sendable(type)) { ast_error(type, "this parameter must be sendable (iso, val or tag)"); r = AST_ERROR; } param = ast_sibling(param); } return r; }
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; }
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; }