static bool reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEPARAMREF); ast_t* ref_name = ast_child(ast); ast_t* param_name = ast_child(typeparam); if(ast_name(ref_name) != ast_name(param_name)) return false; // Keep ephemerality. switch(ast_id(ast_childidx(ast, 2))) { case TK_EPHEMERAL: typearg = consume_type(typearg, TK_NONE); break; case TK_NONE: break; case TK_BORROWED: typearg = alias(typearg); break; default: assert(0); return false; } ast_replace(astp, typearg); return true; }
static void reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEPARAMREF); ast_t* ref_def = (ast_t*)ast_data(ast); ast_t* param_def = (ast_t*)ast_data(typeparam); assert(ref_def != NULL); assert(param_def != NULL); if(ref_def != param_def) return; // Keep ephemerality. switch(ast_id(ast_childidx(ast, 2))) { case TK_EPHEMERAL: typearg = consume_type(typearg, TK_NONE); break; case TK_NONE: break; case TK_BORROWED: typearg = alias(typearg); break; default: assert(0); } ast_replace(astp, typearg); }
bool expr_consume(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, term); ast_t* type = ast_type(term); if(is_typecheck_error(type)) return false; const char* name = NULL; switch(ast_id(term)) { case TK_VARREF: case TK_LETREF: case TK_PARAMREF: { ast_t* id = ast_child(term); name = ast_name(id); break; } case TK_THIS: { name = stringtab("this"); break; } default: ast_error(opt->check.errors, ast, "consume must take 'this', a local, or a parameter"); return false; } // Can't consume from an outer scope while in a loop condition. if((opt->check.frame->loop_cond != NULL) && !ast_within_scope(opt->check.frame->loop_cond, ast, name)) { ast_error(opt->check.errors, ast, "can't consume from an outer scope in a loop condition"); return false; } ast_setstatus(ast, name, SYM_CONSUMED); token_id tcap = ast_id(cap); ast_t* c_type = consume_type(type, tcap); if(c_type == NULL) { ast_error(opt->check.errors, ast, "can't consume to this capability"); ast_error_continue(opt->check.errors, term, "expression type is %s", ast_print_type(type)); return false; } ast_settype(ast, c_type); return true; }
static void reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; pony_assert(ast_id(ast) == TK_TYPEPARAMREF); pony_assert(ast_id(typeparam) == TK_TYPEPARAM); ast_t* ref_def = (ast_t*)ast_data(ast); // We can't compare ref_def and typeparam, as they could be a copy or // a iftype shadowing. However, their data points back to the original // typeparam definition, which can be compared. ref_def = (ast_t*)ast_data(ref_def); typeparam = (ast_t*)ast_data(typeparam); pony_assert(ref_def != NULL); pony_assert(typeparam != NULL); if(ref_def != typeparam) return; // Keep ephemerality. switch(ast_id(ast_childidx(ast, 2))) { case TK_EPHEMERAL: typearg = consume_type(typearg, TK_NONE); break; case TK_NONE: break; case TK_ALIASED: typearg = alias(typearg); break; default: pony_assert(0); } ast_replace(astp, typearg); }
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_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; }
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; }
static void replace_type(ast_t** astp, ast_t* target, ast_t* with) { ast_t* ast = *astp; ast_t* child = ast_child(ast); while(child != NULL) { replace_type(&child, target, with); child = ast_sibling(child); } ast_t* node_type = ast_type(ast); if(node_type != NULL) replace_type(&node_type, target, with); if(ast_id(ast) == ast_id(target)) { switch(ast_id(target)) { case TK_THISTYPE: // Replace `this`. ast_replace(astp, ast_dup(with)); break; case TK_TYPEPARAMREF: { // Replace a typeparamref with a reification. ast_t* target_def = (ast_t*)ast_data(target); ast_t* left_def = (ast_t*)ast_data(ast); if(target_def == left_def) { AST_GET_CHILDREN(ast, id, cap, eph); ast_t* a_with = with; switch(ast_id(eph)) { case TK_EPHEMERAL: a_with = consume_type(with, TK_NONE); break; case TK_BORROWED: a_with = alias(with); break; default: {} } if(a_with == with) a_with = ast_dup(with); ast_replace(astp, a_with); } break; } default: assert(0); } } else if(ast_id(ast) == TK_ARROW) { // Recalculate all arrow types. AST_GET_CHILDREN(ast, left, right); ast_t* r_type = viewpoint_type(left, right); ast_replace(astp, r_type); } }