bool literal_is(ast_t* ast, pass_opt_t* opt) { pony_assert(ast != NULL); pony_assert(ast_id(ast) == TK_IS || ast_id(ast) == TK_ISNT); AST_GET_CHILDREN(ast, left, right); ast_t* l_type = ast_type(left); ast_t* r_type = ast_type(right); if(is_typecheck_error(l_type) || is_typecheck_error(r_type)) return false; if(!is_type_literal(l_type) && !is_type_literal(r_type)) // No literals here. return true; if(is_type_literal(l_type) && !is_type_literal(r_type)) { // Coerce left to type of right. return coerce_literals(&left, r_type, opt); } if(!is_type_literal(l_type) && is_type_literal(r_type)) { // Coerce right to type of left. return coerce_literals(&right, l_type, opt); } // Both sides are literals, that's a problem. pony_assert(is_type_literal(l_type)); pony_assert(is_type_literal(r_type)); ast_error(opt->check.errors, ast, "Cannot infer type of operands"); return false; }
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; }
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_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; }
bool expr_fun(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; AST_GET_CHILDREN(ast, cap, id, typeparams, params, type, can_error, body); if(ast_id(body) == TK_NONE) return true; if(!coerce_literals(&body, type, opt)) return false; bool is_trait = (ast_id(t->frame->type) == TK_TRAIT) || (ast_id(t->frame->type) == TK_INTERFACE) || (ast_id((ast_t*)ast_data(ast)) == TK_TRAIT) || (ast_id((ast_t*)ast_data(ast)) == TK_INTERFACE); // Check partial functions. if(ast_id(can_error) == TK_QUESTION) { // If a partial function, check that we might actually error. if(!is_trait && !ast_canerror(body)) { ast_error(can_error, "function body is not partial but the function is"); return false; } } else { // If not a partial function, check that we can't error. if(ast_canerror(body)) { ast_error(can_error, "function body is partial but the function is not"); show_partiality(body); return false; } } if(!check_primitive_init(t, ast) || !check_finaliser(ast)) return false; switch(ast_id(ast)) { case TK_NEW: return check_fields_defined(ast) && check_main_create(t, ast); case TK_FUN: return check_return_type(ast); default: {} } return true; }
// Infer the types of any literals in the pattern of the given case static bool infer_pattern_type(ast_t* pattern, ast_t* match_expr_type, pass_opt_t* opt) { assert(pattern != NULL); assert(match_expr_type != NULL); if(is_type_literal(match_expr_type)) { ast_error(match_expr_type, "cannot infer type for literal match expression"); return false; } return coerce_literals(&pattern, match_expr_type, opt); }
bool expr_field(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, id, type, init); if(ast_id(init) != TK_NONE) { // Only parameters have initialisers. Field initialisers are moved into // constructors in the sugar pass. assert(ast_id(ast) == TK_PARAM); // 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 info = NULL; if(!is_subtype(init_type, type, &info)) { errorframe_t frame = NULL; ast_error_frame(&frame, init, "default argument is not a subtype of the parameter type"); ast_error_frame(&frame, type, "parameter type: %s", ast_print_type(type)); ast_error_frame(&frame, init, "default argument type: %s", ast_print_type(init_type)); errorframe_append(&frame, &info); errorframe_report(&frame); ast_free_unattached(init_type); return false; } ast_free_unattached(init_type); } ast_settype(ast, type); return true; }
bool expr_fun(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, id, typeparams, params, type, can_error, body); if(ast_id(body) == TK_NONE) return true; if(!coerce_literals(&body, type, opt)) return false; switch(ast_id(ast)) { case TK_NEW: { bool ok = true; if(is_machine_word(type)) { if(!check_return_type(opt, ast)) ok = false; } if(!check_fields_defined(opt, ast)) ok = false; return ok; } case TK_FUN: return check_return_type(opt, ast); default: {} } return true; }
bool literal_call(ast_t* ast, pass_opt_t* options) { assert(ast != NULL); assert(ast_id(ast) == TK_CALL); AST_GET_CHILDREN(ast, positional_args, named_args, receiver); ast_t* recv_type = ast_type(receiver); if(is_typecheck_error(recv_type)) return false; if(ast_id(recv_type) == TK_LITERAL) { ast_error(ast, "Cannot call a literal"); return false; } if(ast_id(recv_type) != TK_OPERATORLITERAL) // Nothing to do return true; lit_op_info_t* op = (lit_op_info_t*)ast_data(recv_type); assert(op != NULL); if(ast_childcount(named_args) != 0) { ast_error(named_args, "Cannot use named arguments with literal operator"); return false; } ast_t* arg = ast_child(positional_args); if(arg != NULL) { if(!unify(arg, options, true)) return false; ast_t* arg_type = ast_type(arg); if(is_typecheck_error(arg_type)) return false; if(ast_id(arg_type) != TK_LITERAL) // Apply argument type to receiver return coerce_literals(&receiver, arg_type, options); if(!op->can_propogate_literal) { ast_error(ast, "Cannot infer operand type"); return false; } } size_t arg_count = ast_childcount(positional_args); if(op->arg_count != arg_count) { ast_error(ast, "Invalid number of arguments to literal operator"); return false; } make_literal_type(ast); return true; }
bool expr_fun(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; AST_GET_CHILDREN(ast, cap, id, typeparams, params, type, can_error, body); if(ast_id(body) == TK_NONE) return true; if(!coerce_literals(&body, type, opt)) return false; bool is_trait = (ast_id(t->frame->type) == TK_TRAIT) || (ast_id(t->frame->type) == TK_INTERFACE) || (ast_id((ast_t*)ast_data(ast)) == TK_TRAIT) || (ast_id((ast_t*)ast_data(ast)) == TK_INTERFACE); // Check partial functions. if(ast_id(can_error) == TK_QUESTION) { // If a partial function, check that we might actually error. ast_t* body_type = ast_type(body); if(body_type == NULL) { // An error has already occurred. assert(get_error_count() > 0); return false; } if(!is_trait && !ast_canerror(body) && (ast_id(body_type) != TK_COMPILE_INTRINSIC)) { ast_error(can_error, "function body is not partial but the function is"); return false; } } else { // If not a partial function, check that we can't error. if(ast_canerror(body)) { ast_error(can_error, "function body is partial but the function is not"); show_partiality(body); return false; } } if(!check_primitive_init(t, ast) || !check_finaliser(t, ast)) return false; switch(ast_id(ast)) { case TK_NEW: { bool ok = true; if(is_machine_word(type)) { if(!check_return_type(ast)) ok = false; } if(!check_fields_defined(ast)) ok = false; if(!check_main_create(t, ast)) ok = false; return ok; } case TK_FUN: return check_return_type(ast); default: {} } return true; }
// Process the given capture and create the AST for the corresponding field. // Returns the create field AST, which must be freed by the caller. // Returns NULL on error. static ast_t* make_capture_field(pass_opt_t* opt, ast_t* capture) { assert(capture != NULL); AST_GET_CHILDREN(capture, id_node, type, value); const char* name = ast_name(id_node); // 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 assert(ast_id(type) == TK_NONE); 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 NULL; } token_id def_id = ast_id(def); if(def_id != TK_ID && def_id != TK_FVAR && def_id != TK_FLET && def_id != TK_PARAM) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } 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 type = alias(ast_type(value)); } else { // Type given, infer literals if(!coerce_literals(&value, type, opt)) return NULL; } if(is_typecheck_error(type)) return NULL; type = sanitise_type(type); BUILD(field, id_node, NODE(TK_FVAR, TREE(id_node) TREE(type) TREE(value) NONE)); // Delegate type return field; }
bool expr_return(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; // return is always the last expression in a sequence assert(ast_sibling(ast) == NULL); if(ast_parent(ast) == t->frame->method_body) { ast_error(ast, "use return only to exit early from a method, not at the end"); return false; } ast_t* type = ast_childidx(t->frame->method, 4); ast_t* body = ast_child(ast); if(!coerce_literals(&body, type, opt)) return false; ast_t* body_type = ast_type(body); if(is_typecheck_error(body_type)) return false; if(is_control_type(body_type)) { ast_error(body, "return value cannot be a control statement"); return false; } bool ok = true; switch(ast_id(t->frame->method)) { case TK_NEW: if(is_this_incomplete(t, ast)) { ast_error(ast, "all fields must be defined before constructor returns"); ok = false; } break; case TK_BE: assert(is_none(body_type)); break; default: { // The body type must be a subtype of the return type, and an alias of // the body type must be a subtype of an alias of the return type. ast_t* a_type = alias(type); ast_t* a_body_type = alias(body_type); errorframe_t info = NULL; if(!is_subtype(body_type, type, &info) || !is_subtype(a_body_type, a_type, &info)) { errorframe_t frame = NULL; ast_t* last = ast_childlast(body); ast_error_frame(&frame, last, "returned value isn't the return type"); ast_error_frame(&frame, type, "function return type: %s", ast_print_type(type)); ast_error_frame(&frame, body_type, "returned value type: %s", ast_print_type(body_type)); errorframe_append(&frame, &info); errorframe_report(&frame); ok = false; } ast_free_unattached(a_type); ast_free_unattached(a_body_type); } } ast_settype(ast, ast_from(ast, TK_RETURN)); ast_inheritflags(ast); return ok; }
// 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; }
static bool check_arg_types(pass_opt_t* opt, ast_t* params, ast_t* positional, bool incomplete, bool partial) { // Check positional args vs params. ast_t* param = ast_child(params); ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) == TK_NONE) { if(partial) { // Don't check missing arguments for partial application. arg = ast_sibling(arg); param = ast_sibling(param); continue; } else { // Pick up a default argument if we can. if(!apply_default_arg(opt, param, arg)) return false; } } ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return false; ast_t* arg_type = ast_type(arg); if(is_typecheck_error(arg_type)) return false; if(is_control_type(arg_type)) { ast_error(opt->check.errors, arg, "can't use a control expression in an argument"); return false; } ast_t* a_type = alias(arg_type); if(incomplete) { ast_t* expr = arg; if(ast_id(arg) == TK_SEQ) expr = ast_child(arg); // If 'this' is incomplete and the arg is 'this', change the type to tag. if((ast_id(expr) == TK_THIS) && (ast_sibling(expr) == NULL)) { ast_t* tag_type = set_cap_and_ephemeral(a_type, TK_TAG, TK_NONE); ast_free_unattached(a_type); a_type = tag_type; } } errorframe_t info = NULL; if(!is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); arg = ast_sibling(arg); param = ast_sibling(param); } 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 ast_result_t declared_ffi(pass_opt_t* opt, ast_t* call, ast_t* decl) { assert(call != NULL); assert(decl != NULL); assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(call, call_name, call_ret_typeargs, args, named_args, call_error); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); // Check args vs params ast_t* param = ast_child(params); ast_t* arg = ast_child(args); while((arg != NULL) && (param != NULL) && ast_id(param) != TK_ELLIPSIS) { ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return AST_ERROR; ast_t* a_type = ast_type(arg); errorframe_t info = NULL; if((a_type != NULL) && !void_star_param(p_type, a_type) && !is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); ast_error_frame(&frame, param, "parameter type: %s", ast_print_type(p_type)); ast_error_frame(&frame, arg, "argument type: %s", ast_print_type(a_type)); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } arg = ast_sibling(arg); param = ast_sibling(param); } if(arg != NULL && param == NULL) { ast_error(opt->check.errors, arg, "too many arguments"); return AST_ERROR; } if(param != NULL && ast_id(param) != TK_ELLIPSIS) { ast_error(opt->check.errors, named_args, "too few arguments"); return AST_ERROR; } for(; arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return AST_ERROR; } } // Check return types ast_t* call_ret_type = ast_child(call_ret_typeargs); ast_t* decl_ret_type = ast_child(decl_ret_typeargs); errorframe_t info = NULL; if((call_ret_type != NULL) && !is_eqtype(call_ret_type, decl_ret_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, call_ret_type, "call return type does not match declaration"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } // Check partiality if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE)) { ast_error(opt->check.errors, call_error, "call is partial but the declaration is not"); return AST_ERROR; } if((ast_id(decl_error) == TK_QUESTION) || (ast_id(call_error) == TK_QUESTION)) { ast_seterror(call); } // Store the declaration so that codegen can generate a non-variadic // signature for the FFI call. ast_setdata(call, decl); ast_settype(call, decl_ret_type); ast_inheritflags(call); return AST_OK; }
bool expr_return(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->method_body == NULL) { ast_error(ast, "return must occur in a method body"); return false; } // return is always the last expression in a sequence assert(ast_sibling(ast) == NULL); if(ast_parent(ast) == t->frame->method_body) { ast_error(ast, "use return only to exit early from a method, not at the end"); return false; } ast_t* type = ast_childidx(t->frame->method, 4); ast_t* body = ast_child(ast); if(!coerce_literals(&body, type, opt)) return false; ast_t* body_type = ast_type(body); if(is_typecheck_error(body_type)) return false; if(is_control_type(body_type)) { ast_error(body, "return value cannot be a control statement"); return false; } bool ok = true; switch(ast_id(t->frame->method)) { case TK_NEW: if(!is_none(body_type)) { ast_error(ast, "return in a constructor must return None"); ok = false; } break; case TK_BE: if(!is_none(body_type)) { ast_error(ast, "return in a behaviour must return None"); ok = false; } break; default: { // The body type must be a subtype of the return type, and an alias of // the body type must be a subtype of an alias of the return type. ast_t* a_type = alias(type); ast_t* a_body_type = alias(body_type); if(!is_subtype(body_type, type) || !is_subtype(a_body_type, a_type)) { ast_t* last = ast_childlast(body); ast_error(last, "returned value isn't the return type"); ast_error(type, "function return type: %s", ast_print_type(type)); ast_error(body_type, "returned value type: %s", ast_print_type(body_type)); ok = false; } ast_free_unattached(a_type); ast_free_unattached(a_body_type); } } ast_settype(ast, ast_from(ast, TK_RETURN)); ast_inheritflags(ast); return ok; }
bool expr_seq(pass_opt_t* opt, ast_t* ast) { bool ok = true; // Any expression other than the last that is still literal is an error for(ast_t* p = ast_child(ast); ast_sibling(p) != NULL; p = ast_sibling(p)) { ast_t* p_type = ast_type(p); if(is_typecheck_error(p_type)) { ok = false; } else if(is_type_literal(p_type)) { ast_error(p, "Cannot infer type of unused literal"); ok = false; } } // We might already have a type due to a return expression. ast_t* type = ast_type(ast); ast_t* last = ast_childlast(ast); if((type != NULL) && !coerce_literals(&last, type, opt)) return false; // Type is unioned with the type of the last child. type = control_type_add_branch(type, last); ast_settype(ast, type); ast_inheritflags(ast); if(!ast_has_scope(ast)) return ok; ast_t* parent = ast_parent(ast); switch(ast_id(parent)) { case TK_TRY: case TK_TRY_NO_CHECK: { // Propagate consumes forward in a try expression. AST_GET_CHILDREN(parent, body, else_clause, then_clause); if(body == ast) { // Push our consumes, but not defines, to the else clause. ast_inheritbranch(else_clause, body); ast_consolidate_branches(else_clause, 2); } else if(else_clause == ast) { // Push our consumes, but not defines, to the then clause. This // includes the consumes from the body. ast_inheritbranch(then_clause, else_clause); ast_consolidate_branches(then_clause, 2); } } default: {} } return ok; }
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; }
static bool check_arg_types(pass_opt_t* opt, ast_t* params, ast_t* positional, bool partial) { // Check positional args vs params. ast_t* param = ast_child(params); ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) == TK_NONE) { if(partial) { // Don't check missing arguments for partial application. arg = ast_sibling(arg); param = ast_sibling(param); continue; } else { // Pick up a default argument if we can. if(!apply_default_arg(opt, param, arg)) return false; } } ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return false; ast_t* arg_type = ast_type(arg); if(is_typecheck_error(arg_type)) return false; if(ast_checkflag(arg, AST_FLAG_JUMPS_AWAY)) { ast_error(opt->check.errors, arg, "can't use a control expression in an argument"); return false; } ast_t* a_type = alias(arg_type); errorframe_t info = NULL; if(!is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); errorframe_append(&frame, &info); if(ast_checkflag(ast_type(arg), AST_FLAG_INCOMPLETE)) ast_error_frame(&frame, arg, "this might be possible if all fields were already defined"); errorframe_report(&frame, opt->check.errors); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); arg = ast_sibling(arg); param = ast_sibling(param); } return true; }