static bool check_receiver_cap(pass_opt_t* opt, ast_t* ast) { 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); // Check receiver cap. ast_t* receiver = ast_child(lhs); // Dig through function qualification. if(ast_id(receiver) == TK_FUNREF || ast_id(receiver) == TK_FUNAPP) receiver = ast_child(receiver); // Receiver type, alias of receiver type, and target type. ast_t* r_type = ast_type(receiver); if(is_typecheck_error(r_type)) return false; ast_t* t_type = set_cap_and_ephemeral(r_type, ast_id(cap), TK_NONE); ast_t* a_type; // If we can recover the receiver, we don't alias it here. bool can_recover = auto_recover_call(ast, r_type, positional, result); bool cap_recover = false; switch(ast_id(cap)) { case TK_ISO: case TK_TRN: case TK_VAL: case TK_TAG: break; case TK_REF: case TK_BOX: cap_recover = true; break; default: assert(0); } if(can_recover && cap_recover) a_type = r_type; else a_type = alias(r_type); errorframe_t info = NULL; bool ok = is_subtype(a_type, t_type, &info, opt); if(!ok) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "receiver type is not a subtype of target type"); ast_error_frame(&frame, receiver, "receiver type: %s", ast_print_type(a_type)); ast_error_frame(&frame, cap, "target type: %s", ast_print_type(t_type)); if(!can_recover && cap_recover && is_subtype(r_type, t_type, NULL, opt)) { ast_error_frame(&frame, ast, "this would be possible if the arguments and return value " "were all sendable"); } errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); } if(a_type != r_type) ast_free_unattached(a_type); ast_free_unattached(r_type); ast_free_unattached(t_type); return ok; }
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs, bool report_errors, pass_opt_t* opt) { ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); while(typeparam != NULL) { if(is_bare(typearg)) { if(report_errors) { ast_error(opt->check.errors, typearg, "a bare type cannot be used as a type argument"); } return false; } switch(ast_id(typearg)) { case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(typearg); if(ast_id(def) == TK_STRUCT) { if(report_errors) { ast_error(opt->check.errors, typearg, "a struct cannot be used as a type argument"); } return false; } break; } case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(typearg); if(def == typeparam) { typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); continue; } break; } default: {} } // Reify the constraint. ast_t* constraint = ast_childidx(typeparam, 1); ast_t* r_constraint = reify(constraint, typeparams, typeargs, opt, true); // A bound type must be a subtype of the constraint. errorframe_t info = NULL; errorframe_t* infop = (report_errors ? &info : NULL); if(!is_subtype_constraint(typearg, r_constraint, infop, opt)) { if(report_errors) { errorframe_t frame = NULL; ast_error_frame(&frame, orig, "type argument is outside its constraint"); ast_error_frame(&frame, typearg, "argument: %s", ast_print_type(typearg)); ast_error_frame(&frame, typeparam, "constraint: %s", ast_print_type(r_constraint)); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); } ast_free_unattached(r_constraint); return false; } ast_free_unattached(r_constraint); // A constructable constraint can only be fulfilled by a concrete typearg. if(is_constructable(constraint) && !is_concrete(typearg)) { if(report_errors) { ast_error(opt->check.errors, orig, "a constructable constraint can " "only be fulfilled by a concrete type argument"); ast_error_continue(opt->check.errors, typearg, "argument: %s", ast_print_type(typearg)); ast_error_continue(opt->check.errors, typeparam, "constraint: %s", ast_print_type(constraint)); } return false; } typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); } pony_assert(typeparam == NULL); pony_assert(typearg == NULL); 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(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); 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; }
static bool check_receiver_cap(pass_opt_t* opt, ast_t* ast, bool* recovered) { AST_GET_CHILDREN(ast, positional, namedargs, question, lhs); ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; AST_GET_CHILDREN(type, cap, typeparams, params, result); // Receiver type, alias of receiver type, and target type. ast_t* r_type = method_receiver_type(lhs); if(is_typecheck_error(r_type)) return false; ast_t* t_type = set_cap_and_ephemeral(r_type, ast_id(cap), TK_NONE); ast_t* a_type; // If we can recover the receiver, we don't alias it here. bool can_recover = auto_recover_call(lhs, r_type, positional, result); bool cap_recover = false; switch(ast_id(cap)) { case TK_ISO: case TK_TRN: case TK_VAL: case TK_TAG: break; case TK_REF: case TK_BOX: cap_recover = true; break; default: pony_assert(0); } if(can_recover && cap_recover) { a_type = r_type; if(recovered != NULL) *recovered = true; } else { a_type = alias(r_type); if(recovered != NULL) *recovered = false; } errorframe_t info = NULL; bool ok = is_subtype(a_type, t_type, &info, opt); if(!ok) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "receiver type is not a subtype of target type"); ast_error_frame(&frame, ast_child(lhs), "receiver type: %s", ast_print_type(a_type)); ast_error_frame(&frame, cap, "target type: %s", ast_print_type(t_type)); errorframe_append(&frame, &info); if(ast_checkflag(ast_type(method_receiver(lhs)), AST_FLAG_INCOMPLETE)) ast_error_frame(&frame, method_receiver(lhs), "this might be possible if all fields were already defined"); if(!can_recover && cap_recover && is_subtype(r_type, t_type, NULL, opt)) { ast_error_frame(&frame, ast, "this would be possible if the arguments and return value " "were all sendable"); } errorframe_report(&frame, opt->check.errors); } if(a_type != r_type) ast_free_unattached(a_type); ast_free_unattached(r_type); ast_free_unattached(t_type); return ok; }
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"); 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); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); arg = ast_sibling(arg); param = ast_sibling(param); } 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_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; }
// 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; }
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; }
static bool is_valid_pattern(pass_opt_t* opt, ast_t* pattern) { if(ast_id(pattern) == TK_NONE) { ast_settype(pattern, ast_from(pattern, TK_DONTCARE)); return true; } ast_t* pattern_type = ast_type(pattern); if(is_control_type(pattern_type)) { ast_error(opt->check.errors, pattern, "not a matchable pattern"); return false; } switch(ast_id(pattern)) { case TK_MATCH_CAPTURE: // Captures are always OK. return true; case TK_TUPLE: { ast_t* pattern_child = ast_child(pattern); // Treat a one element tuple as a normal expression. if(ast_sibling(pattern_child) == NULL) { bool ok = is_valid_pattern(opt, pattern_child); ast_settype(pattern, ast_type(pattern_child)); return ok; } // Check every element pairwise. ast_t* pattern_type = ast_from(pattern, TK_TUPLETYPE); bool ok = true; while(pattern_child != NULL) { if(!is_valid_pattern(opt, pattern_child)) ok = false; ast_append(pattern_type, ast_type(pattern_child)); pattern_child = ast_sibling(pattern_child); } ast_settype(pattern, pattern_type); return ok; } case TK_SEQ: if(ast_childcount(pattern) == 1) // This may be a just a capture. return is_valid_pattern(opt, ast_child(pattern)); // Treat this like other nodes. break; case TK_DONTCARE: // It's always ok not to care. return true; default: break; } // Structural equality, pattern.eq(match). ast_t* fun = lookup(opt, pattern, pattern_type, stringtab("eq")); if(fun == NULL) { ast_error(opt->check.errors, pattern, "this pattern element doesn't support structural equality"); return false; } if(ast_id(fun) != TK_FUN) { ast_error(opt->check.errors, pattern, "eq is not a function on this pattern element"); ast_error_continue(opt->check.errors, fun, "definition of eq is here"); ast_free_unattached(fun); return false; } AST_GET_CHILDREN(fun, cap, id, typeparams, params, result, partial); bool ok = true; if(ast_id(typeparams) != TK_NONE) { ast_error(opt->check.errors, pattern, "polymorphic eq not supported in pattern matching"); ok = false; } if(!is_bool(result)) { ast_error(opt->check.errors, pattern, "eq must return Bool when pattern matching"); ok = false; } if(ast_id(partial) != TK_NONE) { ast_error(opt->check.errors, pattern, "eq cannot be partial when pattern matching"); ok = false; } ast_t* r_type = set_cap_and_ephemeral(pattern_type, ast_id(cap), TK_NONE); ast_t* a_type = alias(pattern_type); errorframe_t info = NULL; if(!is_subtype(a_type, r_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, pattern, "eq cannot be called on this pattern"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ok = false; } ast_t* param = ast_child(params); if(param == NULL || ast_sibling(param) != NULL) { ast_error(opt->check.errors, pattern, "eq must take a single argument when pattern matching"); ok = false; } else { AST_GET_CHILDREN(param, param_id, param_type); ast_settype(pattern, param_type); } ast_free_unattached(r_type); ast_free_unattached(a_type); ast_free_unattached(fun); return ok; }