bool expr_break(typecheck_t* t, ast_t* ast) { if(t->frame->loop_body == NULL) { ast_error(ast, "must be in a loop"); return false; } if(!ast_all_consumes_in_scope(t->frame->loop_body, ast)) return false; // break is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_BREAK)); ast_inheritflags(ast); // Add type to loop. ast_t* body = ast_child(ast); if(is_control_type(ast_type(body))) { ast_error(body, "break value cannot be a control statement"); return false; } ast_t* loop_type = ast_type(t->frame->loop); loop_type = control_type_add_branch(loop_type, body); ast_settype(t->frame->loop, loop_type); return true; }
static bool method_chain(pass_opt_t* opt, ast_t* ast) { if(!method_application(opt, ast, false)) return false; // We check the receiver cap now instead of in method_application because // we need to know whether the receiver was recovered. ast_t* lhs = ast_childidx(ast, 2); ast_t* r_type = method_receiver_type(lhs); if(ast_id(lhs) == TK_FUNCHAIN) { bool recovered; if(!check_receiver_cap(opt, ast, &recovered)) return false; if(!check_nonsendable_recover(opt, ast)) return false; ast_t* f_type = ast_type(lhs); token_id f_cap = ast_id(ast_child(f_type)); ast_t* c_type = chain_type(r_type, f_cap, recovered); ast_settype(ast, c_type); } else { ast_settype(ast, r_type); } return true; }
ast_t* control_type_add_branch(ast_t* control_type, ast_t* branch) { assert(branch != NULL); ast_t* branch_type = ast_type(branch); if(is_typecheck_error(branch_type)) return branch_type; if(is_type_literal(branch_type)) { // The new branch is a literal if(control_type == NULL) control_type = ast_from(branch, TK_LITERAL); if(ast_id(control_type) != TK_LITERAL) { // The current control type is not a literal, fix that ast_t* old_control = control_type; control_type = ast_from(branch, TK_LITERAL); ast_settype(control_type, old_control); } assert(ast_id(control_type) == TK_LITERAL); // Add a literal branch reference to the new branch ast_t* member = ast_from(branch, TK_LITERALBRANCH); ast_setdata(member, (void*)branch); ast_append(control_type, member); ast_t* branch_non_lit = ast_type(branch_type); if(branch_non_lit != NULL) { // The branch's literal type has a non-literal component ast_t* non_literal_type = ast_type(control_type); ast_settype(control_type, type_union(non_literal_type, branch_non_lit)); } return control_type; } if(control_type != NULL && ast_id(control_type) == TK_LITERAL) { // New branch is not literal, but the control structure is // Add new branch type to the control structure's non-literal aspect ast_t* non_literal_type = ast_type(control_type); non_literal_type = type_union(non_literal_type, branch_type); ast_settype(control_type, non_literal_type); return control_type; } // No literals here, just union the types return type_union(control_type, branch_type); }
bool expr_dontcare(ast_t* ast) { // We are a tuple element. That tuple must either be a pattern or the LHS // of an assignment. It can be embedded in other tuples, which may appear // in sequences. ast_t* tuple = ast_parent(ast); if(ast_id(tuple) == TK_TUPLE) { ast_t* parent = ast_parent(tuple); while((ast_id(parent) == TK_TUPLE) || (ast_id(parent) == TK_SEQ)) { tuple = parent; parent = ast_parent(tuple); } switch(ast_id(parent)) { case TK_ASSIGN: { AST_GET_CHILDREN(parent, right, left); if(tuple == left) { ast_settype(ast, ast); return true; } break; } case TK_CASE: { AST_GET_CHILDREN(parent, pattern, guard, body); if(tuple == pattern) { ast_settype(ast, ast); return true; } break; } default: {} } } ast_error(ast, "the don't care token can only appear in a tuple, either on " "the LHS of an assignment or in a pattern"); return false; }
bool expr_match_capture(pass_opt_t* opt, ast_t* ast) { (void)opt; assert(ast != NULL); ast_t* type = ast_childidx(ast, 1); assert(type != NULL); // Capture type is as specified. ast_settype(ast, type); ast_settype(ast_child(ast), type); return true; }
// Setup the type, or lack thereof, for local variable declarations. // This is not really anything to do with traits, but must be done before the // expr pass (to allow initialisation references to the variable type) but // after the name pass (to get temporal capabilities). static void local_types(ast_t* ast) { assert(ast != NULL); // Setup type or mark as inferred now to allow calling create on a // non-inferred local to initialise itself AST_GET_CHILDREN(ast, id, type); assert(type != NULL); if(ast_id(type) == TK_NONE) type = ast_from(id, TK_INFERTYPE); ast_settype(id, type); ast_settype(ast, type); }
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; }
bool expr_recover(ast_t* ast) { AST_GET_CHILDREN(ast, cap, expr); ast_t* type = ast_type(expr); if(is_typecheck_error(type)) return false; if(is_type_literal(type)) { make_literal_type(ast); return true; } ast_t* r_type = recover_type(type, ast_id(cap)); if(r_type == NULL) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "can't recover to this capability"); ast_error_frame(&frame, expr, "expression type is %s", ast_print_type(type)); errorframe_report(&frame); return false; } ast_settype(ast, r_type); ast_inheritflags(ast); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), expr); return true; }
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; }
bool expr_recover(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, expr); ast_t* type = ast_type(expr); if(is_typecheck_error(type)) return false; if(is_type_literal(type)) { make_literal_type(ast); return true; } ast_t* r_type = recover_type(type, ast_id(cap)); if(r_type == NULL) { ast_error(opt->check.errors, ast, "can't recover to this capability"); ast_error_continue(opt->check.errors, expr, "expression type is %s", ast_print_type(type)); return false; } ast_settype(ast, r_type); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), expr); return true; }
bool expr_identityof(pass_opt_t* opt, ast_t* ast) { ast_t* expr = ast_child(ast); switch(ast_id(expr)) { case TK_FVARREF: case TK_FLETREF: case TK_EMBEDREF: case TK_VARREF: case TK_LETREF: case TK_PARAMREF: case TK_THIS: break; default: ast_error(ast, "identity must be for a field, local, parameter or this"); return false; } // Set the type to U64. ast_t* type = type_builtin(opt, expr, "U64"); ast_settype(ast, type); return true; }
bool expr_param(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); ast_settype(ast, type); bool ok = true; if(ast_id(init) != TK_NONE) { // Initialiser type must match declared type. if(!coerce_literals(&init, type, opt)) return false; ast_t* init_type = ast_type(init); if(is_typecheck_error(init_type)) return false; init_type = alias(init_type); errorframe_t err = NULL; if(!is_subtype(init_type, type, &err, opt)) { errorframe_t err2 = NULL; ast_error_frame(&err2, init, "default argument is not a subtype of the parameter type"); errorframe_append(&err2, &err); errorframe_report(&err2, opt->check.errors); ok = false; } ast_free_unattached(init_type); } return ok; }
bool expr_compiler_intrinsic(typecheck_t* t, ast_t* ast) { if(t->frame->method_body == NULL) { ast_error(ast, "a compiler intrinsic must be a method body"); return false; } ast_t* child = ast_child(t->frame->method_body); // Allow a docstring before the compiler_instrinsic. if(ast_id(child) == TK_STRING) child = ast_sibling(child); if((child != ast) || (ast_sibling(child) != NULL)) { ast_error(ast, "a compiler intrinsic must be the entire body"); return false; } // Disable debuglocs on calls to this method. ast_setdebug(t->frame->method, false); ast_settype(ast, ast_from(ast, TK_COMPILER_INTRINSIC)); return true; }
static bool method_call(pass_opt_t* opt, ast_t* ast) { if(!method_application(opt, ast, false)) return false; AST_GET_CHILDREN(ast, positional, namedargs, lhs); ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; AST_GET_CHILDREN(type, cap, typeparams, params, result); ast_settype(ast, result); ast_inheritflags(ast); switch(ast_id(lhs)) { case TK_NEWBEREF: case TK_BEREF: ast_setsend(ast); return true; case TK_NEWREF: case TK_FUNREF: ast_setmightsend(ast); return true; default: {} } assert(0); return false; }
ast_t* builder_add_type(builder_t* builder, const char* type_of, const char* description) { assert(type_of != NULL); assert(description != NULL); if(builder == NULL) return NULL; ast_t* type_parent = builder_find_sub_tree(builder, type_of); if(type_parent == NULL) { error(NULL, 0, 0, "Node with attribute {def %s} not found", type_of); return NULL; } assert(ast_type(type_parent) == NULL); ast_t* ast = builder_add_ast(builder, description); if(ast == NULL) return NULL; // Success, add new type to builder ast_settype(type_parent, ast); return ast; }
bool expr_digestof(pass_opt_t* opt, ast_t* ast) { ast_t* expr = ast_child(ast); switch(ast_id(expr)) { case TK_FVARREF: case TK_FLETREF: case TK_EMBEDREF: case TK_VARREF: case TK_LETREF: case TK_PARAMREF: case TK_THIS: break; default: ast_error(opt->check.errors, ast, "can only get the digest of a field, local, parameter or this"); return false; } // Set the type to U64. ast_t* type = type_builtin(opt, expr, "U64"); ast_settype(ast, type); return true; }
static bool expr_addressof_ffi(pass_opt_t* opt, ast_t* ast) { ast_t* expr = ast_child(ast); switch(ast_id(expr)) { case TK_FVARREF: case TK_VARREF: case TK_FLETREF: case TK_LETREF: break; case TK_PARAMREF: ast_error(ast, "can't take the address of a function parameter"); return false; default: ast_error(ast, "can only take the address of a field or local variable"); return false; } // Set the type to Pointer[ast_type(expr)]. ast_t* expr_type = ast_type(expr); if(is_typecheck_error(expr_type)) return false; ast_t* type = type_pointer_to(opt, expr_type); ast_settype(ast, type); return true; }
bool expr_field(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); if(ast_id(init) != TK_NONE) { // Initialiser type must match declared type. if(!coerce_literals(&init, type, opt)) return false; ast_t* init_type = ast_type(init); if(is_typecheck_error(init_type)) return false; init_type = alias(init_type); if(!is_subtype(init_type, type)) { ast_error(init, "field/param initialiser is not a subtype of the field/param type"); ast_error(type, "field/param type: %s", ast_print_type(type)); ast_error(init, "initialiser type: %s", ast_print_type(init_type)); ast_free_unattached(init_type); return false; } ast_free_unattached(init_type); } ast_settype(ast, type); return true; }
static bool check_type_params(ast_t** astp) { ast_t* lhs = *astp; ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; ast_t* typeparams = ast_childidx(type, 1); assert(ast_id(type) == TK_FUNTYPE); if(ast_id(typeparams) == TK_NONE) return true; BUILD(typeargs, typeparams, NODE(TK_TYPEARGS)); if(!check_constraints(typeparams, typeargs, true)) { ast_free_unattached(typeargs); return false; } type = reify(type, typeparams, typeargs); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); REPLACE(astp, NODE(ast_id(lhs), TREE(lhs) TREE(typeargs))); ast_settype(*astp, type); return true; }
bool expr_compile_error(ast_t* ast) { // compile_error is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_COMPILE_ERROR)); return true; }
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; }
bool expr_error(pass_opt_t* opt, ast_t* ast) { (void)opt; // error is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_ERROR)); return true; }
bool expr_break(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->loop_body == NULL) { ast_error(opt->check.errors, ast, "must be in a loop"); return false; } errorframe_t errorf = NULL; if(!ast_all_consumes_in_scope(t->frame->loop_body, ast, &errorf)) { errorframe_report(&errorf, opt->check.errors); return false; } // break is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_BREAK)); // Add type to loop. ast_t* body = ast_child(ast); if(ast_id(body) != TK_NONE) { if(is_control_type(ast_type(body))) { ast_error(opt->check.errors, body, "break value cannot be a control statement"); return false; } ast_t* loop_type = ast_type(t->frame->loop); // If there is no body the break will jump to the else branch, whose type // has already been added to the loop type. loop_type = control_type_add_branch(opt, loop_type, body); ast_settype(t->frame->loop, loop_type); } return true; }
bool expr_repeat(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, body, cond, else_clause); ast_t* body_type = ast_type(body); ast_t* cond_type = ast_type(cond); ast_t* else_type = ast_type(else_clause); if(is_typecheck_error(cond_type)) return false; if(!is_bool(cond_type)) { ast_error(opt->check.errors, cond, "condition must be a Bool"); return false; } if(is_typecheck_error(body_type) || is_typecheck_error(else_type)) return false; // All consumes have to be in scope when the loop body finishes. errorframe_t errorf = NULL; if(!ast_all_consumes_in_scope(body, body, &errorf)) { errorframe_report(&errorf, opt->check.errors); return false; } // Union with any existing type due to a break expression. ast_t* type = ast_type(ast); // No symbol status is inherited from the loop body or condition. Nothing // from outside can be consumed, and definitions inside may not occur. if(!is_control_type(body_type)) type = control_type_add_branch(opt, type, body); if(!is_control_type(else_type)) { type = control_type_add_branch(opt, type, else_clause); ast_inheritbranch(ast, else_clause); // Use a branch count of two instead of one. This means we will pick up any // consumes, but not any definitions, since definitions may not occur. ast_consolidate_branches(ast, 2); } if(type == NULL) type = ast_from(ast, TK_REPEAT); ast_settype(ast, type); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
bool expr_if(pass_opt_t* opt, ast_t* ast) { ast_t* cond = ast_child(ast); ast_t* left = ast_sibling(cond); ast_t* right = ast_sibling(left); ast_t* cond_type = ast_type(cond); if(is_typecheck_error(cond_type)) return false; if(!is_bool(cond_type)) { ast_error(cond, "condition must be a Bool"); return false; } ast_t* l_type = ast_type(left); ast_t* r_type = ast_type(right); ast_t* type = NULL; size_t branch_count = 0; if(!is_control_type(l_type)) { type = control_type_add_branch(type, left); ast_inheritbranch(ast, left); branch_count++; } if(!is_control_type(r_type)) { type = control_type_add_branch(type, right); ast_inheritbranch(ast, right); branch_count++; } if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_IF); } ast_settype(ast, type); ast_inheritflags(ast); ast_consolidate_branches(ast, branch_count); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
bool expr_literal(pass_opt_t* opt, ast_t* ast, const char* name) { ast_t* type = type_builtin(opt, ast, name); if(is_typecheck_error(type)) return false; ast_settype(ast, type); return true; }
bool expr_try(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, body, else_clause, then_clause); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); ast_t* then_type = ast_type(then_clause); if(is_typecheck_error(body_type) || is_typecheck_error(else_type) || is_typecheck_error(then_type)) return false; ast_t* type = NULL; if(!is_control_type(body_type)) type = control_type_add_branch(opt, type, body); if(!is_control_type(else_type)) type = control_type_add_branch(opt, type, else_clause); if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(opt->check.errors, ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_TRY); } // The then clause does not affect the type of the expression. if(is_control_type(then_type)) { ast_error(opt->check.errors, then_clause, "then clause always terminates the function"); return false; } if(is_type_literal(then_type)) { ast_error(opt->check.errors, then_clause, "Cannot infer type of unused literal"); return false; } ast_settype(ast, type); literal_unify_control(ast, opt); // Push the symbol status from the then clause to our parent scope. ast_inheritstatus(ast_parent(ast), then_clause); return true; }
bool expr_field(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); // An embedded field must have a known, non-actor type. if(ast_id(ast) == TK_EMBED) { if(!is_known(type) || is_actor(type)) { ast_error(ast, "embedded fields must always be primitives or classes"); return false; } } if(ast_id(init) != TK_NONE) { // Initialiser type must match declared type. if(!coerce_literals(&init, type, opt)) return false; ast_t* init_type = ast_type(init); if(is_typecheck_error(init_type)) return false; init_type = alias(init_type); if(!is_subtype(init_type, type)) { ast_error(init, "field/param initialiser is not a subtype of the field/param type"); ast_error(type, "field/param type: %s", ast_print_type(type)); ast_error(init, "initialiser type: %s", ast_print_type(init_type)); ast_free_unattached(init_type); return false; } // If it's an embedded field, check for a constructor result. if(ast_id(ast) == TK_EMBED) { if((ast_id(init) != TK_CALL) || (ast_id(ast_childidx(init, 2)) != TK_NEWREF)) { ast_error(ast, "an embedded field must be initialised using a constructor"); return false; } } ast_free_unattached(init_type); } ast_settype(ast, type); return true; }
static bool method_chain(pass_opt_t* opt, ast_t* ast) { if(!method_application(opt, ast, false)) return false; AST_GET_CHILDREN(ast, positional, namedargs, question, lhs); ast_t* type = ast_type(lhs); if(ast_id(ast_child(type)) == TK_AT) { ast_error(opt->check.errors, ast, "a bare method cannot be chained"); return false; } // We check the receiver cap now instead of in method_application because // we need to know whether the receiver was recovered. ast_t* r_type = method_receiver_type(lhs); if(ast_id(lhs) == TK_FUNCHAIN) { bool recovered; if(!check_receiver_cap(opt, ast, &recovered)) return false; if(!check_nonsendable_recover(opt, ast)) return false; ast_t* f_type = ast_type(lhs); token_id f_cap = ast_id(ast_child(f_type)); ast_t* c_type = chain_type(r_type, f_cap, recovered); ast_settype(ast, c_type); } else { ast_settype(ast, r_type); } return true; }
bool expr_local(typecheck_t* t, ast_t* ast) { assert(t != NULL); assert(ast != NULL); AST_GET_CHILDREN(ast, id, type); assert(type != NULL); if(ast_id(type) == TK_NONE) { // No type specified, check we can infer if(!is_assigned_to(ast, false)) { if(t->frame->pattern != NULL) ast_error(ast, "cannot infer type of capture variables"); else ast_error(ast, "locals must specify a type or be assigned a value"); return false; } type = ast_from(id, TK_INFERTYPE); } else if(ast_id(ast) == TK_LET && t->frame->pattern == NULL) { // Let, check we have a value assigned if(!is_assigned_to(ast, false)) { ast_error(ast, "can't declare a let local without assigning to it"); return false; } } ast_settype(id, type); ast_settype(ast, type); return true; }