bool reify_defaults(ast_t* typeparams, ast_t* typeargs, bool errors, pass_opt_t* opt) { assert( (ast_id(typeparams) == TK_TYPEPARAMS) || (ast_id(typeparams) == TK_NONE) ); assert( (ast_id(typeargs) == TK_TYPEARGS) || (ast_id(typeargs) == TK_NONE) ); size_t param_count = ast_childcount(typeparams); size_t arg_count = ast_childcount(typeargs); if(param_count == arg_count) return true; if(param_count < arg_count) { if(errors) { ast_error(opt->check.errors, typeargs, "too many type arguments"); ast_error_continue(opt->check.errors, typeparams, "definition is here"); } return false; } // Pick up default type arguments if they exist. ast_setid(typeargs, TK_TYPEARGS); ast_t* typeparam = ast_childidx(typeparams, arg_count); while(typeparam != NULL) { ast_t* defarg = ast_childidx(typeparam, 2); if(ast_id(defarg) == TK_NONE) break; ast_append(typeargs, defarg); typeparam = ast_sibling(typeparam); } if(typeparam != NULL) { if(errors) { ast_error(opt->check.errors, typeargs, "not enough type arguments"); ast_error_continue(opt->check.errors, typeparams, "definition is here"); } return false; } return true; }
// Process the given provides type static bool flatten_provided_type(pass_opt_t* opt, ast_t* provides_type, ast_t* error_at, ast_t* list_parent, ast_t** list_end) { pony_assert(error_at != NULL); pony_assert(provides_type != NULL); pony_assert(list_parent != NULL); pony_assert(list_end != NULL); switch(ast_id(provides_type)) { case TK_PROVIDES: case TK_ISECTTYPE: // Flatten all children for(ast_t* p = ast_child(provides_type); p != NULL; p = ast_sibling(p)) { if(!flatten_provided_type(opt, p, error_at, list_parent, list_end)) return false; } return true; case TK_NOMINAL: { // Check type is a trait or interface ast_t* def = (ast_t*)ast_data(provides_type); pony_assert(def != NULL); if(ast_id(def) != TK_TRAIT && ast_id(def) != TK_INTERFACE) { ast_error(opt->check.errors, error_at, "can only provide traits and interfaces"); ast_error_continue(opt->check.errors, provides_type, "invalid type here"); return false; } // Add type to new provides list ast_list_append(list_parent, list_end, provides_type); ast_setdata(*list_end, ast_data(provides_type)); return true; } default: ast_error(opt->check.errors, error_at, "provides type may only be an intersect of traits and interfaces"); ast_error_continue(opt->check.errors, provides_type, "invalid type here"); return false; } }
static bool check_partial_ffi_call(pass_opt_t* opt, ast_t* ast) { pony_assert(ast_id(ast) == TK_FFICALL); AST_GET_CHILDREN(ast, call_name, call_ret_typeargs, args, named_args, call_error); // The expr pass (expr_ffi) should have stored the declaration here, if found. ast_t* decl = (ast_t*)ast_data(ast); if(decl == NULL) { if(ast_id(call_error) == TK_QUESTION) ast_seterror(ast); } else { pony_assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); if((ast_id(decl_error) == TK_QUESTION) || (ast_id(call_error) == TK_QUESTION)) ast_seterror(ast); 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"); ast_error_continue(opt->check.errors, decl_error, "declaration is here"); return false; } } return true; }
static ast_result_t syntax_lambda(pass_opt_t* opt, ast_t* ast) { assert(ast_id(ast) == TK_LAMBDA); AST_GET_CHILDREN(ast, cap, name, typeparams, params, captures, type, can_error, body); switch(ast_id(type)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: { ast_error(opt->check.errors, type, "lambda return type: %s", ast_print_type(type)); ast_error_continue(opt->check.errors, type, "lambda return type " "cannot be capability"); return AST_ERROR; } default: {} } return AST_OK; }
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; }
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg) { // Pick up a default argument. AST_GET_CHILDREN(param, id, type, def_arg); if(ast_id(def_arg) == TK_NONE) { ast_error(opt->check.errors, arg, "not enough arguments"); ast_error_continue(opt->check.errors, param, "definition is here"); return false; } if(ast_id(def_arg) == TK_LOCATION) { // Default argument is __loc. Expand call location. ast_t* location = expand_location(arg); ast_add(arg, location); if(!ast_passes_subtree(&location, opt, PASS_EXPR)) return false; } else { // Just use default argument. ast_add(arg, def_arg); } ast_setid(arg, TK_SEQ); if(!expr_seq(opt, arg)) return false; return true; }
static bool apply_named_args(pass_opt_t* opt, ast_t* params, ast_t* positional, ast_t* namedargs) { ast_t* namedarg = ast_pop(namedargs); while(namedarg != NULL) { AST_GET_CHILDREN(namedarg, arg_id, arg); ast_t* param = ast_child(params); size_t param_index = 0; while(param != NULL) { AST_GET_CHILDREN(param, param_id); if(ast_name(arg_id) == ast_name(param_id)) break; param = ast_sibling(param); param_index++; } if(param == NULL) { if(ast_id(namedarg) == TK_UPDATEARG) { ast_error(opt->check.errors, arg_id, "cannot use sugar, update() has no parameter named \"value\""); return false; } ast_error(opt->check.errors, arg_id, "not a parameter name"); return false; } ast_t* arg_replace = ast_childidx(positional, param_index); if(ast_id(arg_replace) != TK_NONE) { ast_error(opt->check.errors, arg_id, "named argument is already supplied"); ast_error_continue(opt->check.errors, arg_replace, "supplied argument is here"); return false; } // Extract named argument expression to avoid copying it ast_free(ast_pop(namedarg)); // ID arg = ast_pop(namedarg); // Expression ast_replace(&arg_replace, arg); namedarg = ast_pop(namedargs); } ast_setid(namedargs, TK_NONE); 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; }
static void report_ffi_type_err(compile_t* c, ffi_decl_t* decl, ast_t* ast, const char* name) { ast_error(c->opt->check.errors, ast, "conflicting declarations for FFI function: %s have incompatible types", name); if(decl != NULL) ast_error_continue(c->opt->check.errors, decl->decl, "first declaration is " "here"); }
static bool check_embed_construction(pass_opt_t* opt, ast_t* left, ast_t* right) { bool result = true; if(ast_id(left) == TK_EMBEDREF) { if(!is_expr_constructor(right)) { ast_error(opt->check.errors, left, "an embedded field must be assigned using a constructor"); ast_error_continue(opt->check.errors, right, "the assigned expression is here"); return false; } } else if(ast_id(left) == TK_TUPLE) { if(ast_id(right) == TK_TUPLE) { ast_t* l_child = ast_child(left); ast_t* r_child = ast_child(right); while(l_child != NULL) { if(ast_id(l_child) != TK_DONTCARE) { assert((ast_id(l_child) == TK_SEQ) && (ast_id(r_child) == TK_SEQ)); ast_t* l_member = ast_childlast(l_child); ast_t* r_member = ast_childlast(r_child); if(!check_embed_construction(opt, l_member, r_member)) result = false; } l_child = ast_sibling(l_child); r_child = ast_sibling(r_child); } assert(r_child == NULL); } else if(tuple_contains_embed(left)) { ast_error(opt->check.errors, left, "an embedded field must be assigned using a constructor"); ast_error_continue(opt->check.errors, right, "the assigned expression isn't a tuple literal"); } } return result; }
static void show_send(pass_opt_t* opt, ast_t* ast) { ast_t* child = ast_child(ast); while(child != NULL) { if(ast_cansend(child) || ast_mightsend(child)) show_send(opt, child); child = ast_sibling(child); } if(ast_id(ast) == TK_CALL) { if(ast_cansend(ast)) ast_error_continue(opt->check.errors, ast, "a message can be sent here"); else if(ast_mightsend(ast)) ast_error_continue(opt->check.errors, ast, "a message might be sent here"); } }
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; }
ast_result_t flatten_typeparamref(pass_opt_t* opt, ast_t* ast) { ast_t* cap_ast = cap_fetch(ast); token_id cap = ast_id(cap_ast); typeparam_set_cap(ast); token_id set_cap = ast_id(cap_ast); if((cap != TK_NONE) && (cap != set_cap)) { ast_t* def = (ast_t*)ast_data(ast); ast_t* constraint = typeparam_constraint(ast); if(constraint != NULL) { ast_error(opt->check.errors, cap_ast, "can't specify a capability on a " "type parameter that differs from the constraint"); ast_error_continue(opt->check.errors, constraint, "constraint definition is here"); if(ast_parent(constraint) != def) { ast_error_continue(opt->check.errors, def, "type parameter definition is here"); } } else { ast_error(opt->check.errors, cap_ast, "a type parameter with no " "constraint can only have #any as its capability"); ast_error_continue(opt->check.errors, def, "type parameter definition is here"); } return AST_ERROR; } return AST_OK; }
/** * Make sure the definition of something occurs before its use. This is for * both fields and local variable. */ bool def_before_use(pass_opt_t* opt, ast_t* def, ast_t* use, const char* name) { if((ast_line(def) > ast_line(use)) || ((ast_line(def) == ast_line(use)) && (ast_pos(def) > ast_pos(use)))) { ast_error(opt->check.errors, use, "declaration of '%s' appears after use", name); ast_error_continue(opt->check.errors, def, "declaration of '%s' appears here", name); return false; } return true; }
static bool extend_positional_args(pass_opt_t* opt, ast_t* params, ast_t* positional) { // Fill out the positional args to be as long as the param list. size_t param_len = ast_childcount(params); size_t arg_len = ast_childcount(positional); if(arg_len > param_len) { ast_error(opt->check.errors, positional, "too many arguments"); ast_error_continue(opt->check.errors, params, "definition is here"); return false; } while(arg_len < param_len) { ast_setid(positional, TK_POSITIONALARGS); ast_append(positional, ast_from(positional, TK_NONE)); arg_len++; } return true; }
static bool show_partiality(pass_opt_t* opt, ast_t* ast) { ast_t* child = ast_child(ast); bool found = false; while(child != NULL) { if(ast_canerror(child)) found |= show_partiality(opt, child); child = ast_sibling(child); } if(found) return true; if(ast_canerror(ast)) { ast_error_continue(opt->check.errors, ast, "an error can be raised here"); return true; } return false; }
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; }
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err); bool err = (ast_id(can_err) == TK_QUESTION); // Get the function name, +1 to skip leading @ const char* f_name = ast_name(id) + 1; deferred_reification_t* reify = c->frame->reify; // Get the return type. ast_t* type = deferred_reify(reify, ast_type(ast), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); ast_free_unattached(type); // Get the function. First check if the name is in use by a global and error // if it's the case. ffi_decl_t* ffi_decl; bool is_func = false; LLVMValueRef func = LLVMGetNamedGlobal(c->module, f_name); if(func == NULL) { func = LLVMGetNamedFunction(c->module, f_name); is_func = true; } if(func == NULL) { // If we have no prototype, declare one. ast_t* decl = (ast_t*)ast_data(ast); if(decl != NULL) { // Define using the declared types. AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_err); err = (ast_id(decl_err) == TK_QUESTION); func = declare_ffi(c, f_name, t, decl_params, false); } else if(!strncmp(f_name, "llvm.", 5) || !strncmp(f_name, "internal.", 9)) { // Intrinsic, so use the exact types we supply. func = declare_ffi(c, f_name, t, args, true); } else { // Make it varargs. func = declare_ffi_vararg(c, f_name, t); } size_t index = HASHMAP_UNKNOWN; #ifndef PONY_NDEBUG ffi_decl_t k; k.func = func; ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index); pony_assert(ffi_decl == NULL); #endif ffi_decl = POOL_ALLOC(ffi_decl_t); ffi_decl->func = func; ffi_decl->decl = (decl != NULL) ? decl : ast; ffi_decls_putindex(&c->ffi_decls, ffi_decl, index); } else { ffi_decl_t k; k.func = func; size_t index = HASHMAP_UNKNOWN; ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index); if((ffi_decl == NULL) && (!is_func || LLVMHasMetadataStr(func, "pony.abi"))) { ast_error(c->opt->check.errors, ast, "cannot use '%s' as an FFI name: " "name is already in use by the internal ABI", f_name); return NULL; } pony_assert(is_func); } // Generate the arguments. int count = (int)ast_childcount(args); size_t buf_size = count * sizeof(LLVMValueRef); LLVMValueRef* f_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* f_params = NULL; bool vararg = (LLVMIsFunctionVarArg(f_type) != 0); if(!vararg) { if(count != (int)LLVMCountParamTypes(f_type)) { ast_error(c->opt->check.errors, ast, "conflicting declarations for FFI function: declarations have an " "incompatible number of parameters"); if(ffi_decl != NULL) ast_error_continue(c->opt->check.errors, ffi_decl->decl, "first " "declaration is here"); return NULL; } f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, f_params); } ast_t* arg = ast_child(args); for(int i = 0; i < count; i++) { f_args[i] = gen_expr(c, arg); if(!vararg) f_args[i] = cast_ffi_arg(c, ffi_decl, ast, f_args[i], f_params[i], "parameters"); if(f_args[i] == NULL) { ponyint_pool_free_size(buf_size, f_args); return NULL; } arg = ast_sibling(arg); } // If we can error out and we have an invoke target, generate an invoke // instead of a call. LLVMValueRef result; codegen_debugloc(c, ast); if(err && (c->frame->invoke_target != NULL)) result = invoke_fun(c, func, f_args, count, "", false); else result = LLVMBuildCall(c->builder, func, f_args, count, ""); codegen_debugloc(c, NULL); ponyint_pool_free_size(buf_size, f_args); if(!vararg) ponyint_pool_free_size(buf_size, f_params); compile_type_t* c_t = (compile_type_t*)t->c_type; // Special case a None return value, which is used for void functions. bool isnone = is_none(t->ast); bool isvoid = LLVMGetReturnType(f_type) == c->void_type; if(isnone && isvoid) { result = c_t->instance; } else if(isnone != isvoid) { report_ffi_type_err(c, ffi_decl, ast, "return values"); return NULL; } result = cast_ffi_arg(c, ffi_decl, ast, result, c_t->use_type, "return values"); result = gen_assign_cast(c, c_t->use_type, result, t->ast_cap); return result; }
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(ast_id(typearg) == TK_TYPEPARAMREF) { ast_t* def = (ast_t*)ast_data(typearg); if(def == typeparam) { typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); continue; } } // Reify the constraint. ast_t* constraint = ast_childidx(typeparam, 1); ast_t* bind_constraint = bind_type(constraint); ast_t* r_constraint = reify(bind_constraint, typeparams, typeargs, opt); if(bind_constraint != r_constraint) ast_free_unattached(bind_constraint); // A bound type must be a subtype of the constraint. errorframe_t info = NULL; if(!is_subtype(typearg, r_constraint, report_errors ? &info : NULL, opt)) { if(report_errors) { ast_error(opt->check.errors, orig, "type argument is outside its constraint"); 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(r_constraint)); } 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); } assert(typeparam == NULL); assert(typearg == NULL); return true; }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { pony_assert(astp != NULL); ast_t* ast = *astp; pony_assert(ast != NULL); AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures, ret_type, raises, body, obj_cap); ast_t* annotation = ast_consumeannotation(ast); // Try to find an antecedent type, and find possible lambda interfaces in it. ast_t* antecedent_type = find_antecedent_type(opt, ast, NULL); astlist_t* possible_fun_defs = NULL; astlist_t* possible_obj_caps = NULL; if(!is_typecheck_error(antecedent_type)) find_possible_fun_defs(opt, antecedent_type, &possible_fun_defs, &possible_obj_caps); // If there's more than one possible fun defs, rule out impossible ones by // comparing each fun def by some basic criteria against the lambda, // creating a new list containing only the remaining possibilities. if(astlist_length(possible_fun_defs) > 1) { astlist_t* new_fun_defs = NULL; astlist_t* new_obj_caps = NULL; astlist_t* fun_def_cursor = possible_fun_defs; astlist_t* obj_cap_cursor = possible_obj_caps; for(; (fun_def_cursor != NULL) && (obj_cap_cursor != NULL); fun_def_cursor = astlist_next(fun_def_cursor), obj_cap_cursor = astlist_next(obj_cap_cursor)) { ast_t* fun_def = astlist_data(fun_def_cursor); ast_t* def_obj_cap = astlist_data(obj_cap_cursor); if(is_typecheck_error(fun_def)) continue; AST_GET_CHILDREN(fun_def, def_receiver_cap, def_name, def_t_params, def_params, def_ret_type, def_raises); // Must have the same number of parameters. if(ast_childcount(params) != ast_childcount(def_params)) continue; // Must have a supercap of the def's receiver cap (if present). if((ast_id(receiver_cap) != TK_NONE) && !is_cap_sub_cap( ast_id(def_receiver_cap), TK_NONE, ast_id(receiver_cap), TK_NONE) ) continue; // Must have a supercap of the def's object cap (if present). if((ast_id(obj_cap) != TK_NONE) && !is_cap_sub_cap(ast_id(obj_cap), TK_NONE, ast_id(def_obj_cap), TK_NONE)) continue; // TODO: This logic could potentially be expanded to do deeper // compatibility checks, but checks involving subtyping here would be // difficult, because the lambda's AST is not caught up yet in the passes. new_fun_defs = astlist_push(new_fun_defs, fun_def); new_obj_caps = astlist_push(new_obj_caps, def_obj_cap); } astlist_free(possible_fun_defs); astlist_free(possible_obj_caps); possible_fun_defs = new_fun_defs; possible_obj_caps = new_obj_caps; } if(astlist_length(possible_fun_defs) == 1) { ast_t* fun_def = astlist_data(possible_fun_defs); ast_t* def_obj_cap = astlist_data(possible_obj_caps); // Try to complete the lambda's type info by inferring from the lambda type. if(!is_typecheck_error(fun_def)) { // Infer the object cap, receiver cap, and return type if unspecified. if(ast_id(obj_cap) == TK_NONE) ast_replace(&obj_cap, def_obj_cap); if(ast_id(receiver_cap) == TK_NONE) ast_replace(&receiver_cap, ast_child(fun_def)); if(ast_id(ret_type) == TK_NONE) ast_replace(&ret_type, ast_childidx(fun_def, 4)); // Infer the type of any parameters that were left unspecified. ast_t* param = ast_child(params); ast_t* def_param = ast_child(ast_childidx(fun_def, 3)); while((param != NULL) && (def_param != NULL)) { ast_t* param_id = ast_child(param); ast_t* param_type = ast_sibling(param_id); // Convert a "_" parameter to whatever the expected parameter is. if(is_name_dontcare(ast_name(param_id))) { ast_replace(¶m_id, ast_child(def_param)); ast_replace(¶m_type, ast_childidx(def_param, 1)); } // Give a type-unspecified parameter the type of the expected parameter. else if(ast_id(param_type) == TK_NONE) { ast_replace(¶m_type, ast_childidx(def_param, 1)); } param = ast_sibling(param); def_param = ast_sibling(def_param); } } ast_free_unattached(fun_def); } astlist_free(possible_obj_caps); // If any parameters still have no type specified, it's an error. ast_t* param = ast_child(params); while(param != NULL) { if(ast_id(ast_childidx(param, 1)) == TK_NONE) { ast_error(opt->check.errors, param, "a lambda parameter must specify a type or be inferable from context"); if(astlist_length(possible_fun_defs) > 1) { for(astlist_t* fun_def_cursor = possible_fun_defs; fun_def_cursor != NULL; fun_def_cursor = astlist_next(fun_def_cursor)) { ast_error_continue(opt->check.errors, astlist_data(fun_def_cursor), "this lambda interface is inferred, but it's not the only one"); } } astlist_free(possible_fun_defs); return false; } param = ast_sibling(param); } astlist_free(possible_fun_defs); bool bare = ast_id(ast) == TK_BARELAMBDA; ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; if(bare) pony_assert(ast_id(captures) == TK_NONE); // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = NULL; bool ok = make_capture_field(opt, p, &field); if(field != NULL) ast_list_append(members, &last_member, field); else if(!ok) // An error occurred, just keep going to potentially find more errors failed = true; } if(failed) { ast_free(members); return false; } // Stop the various elements being marked as preserve ast_clearflag(t_params, AST_FLAG_PRESERVE); ast_clearflag(params, AST_FLAG_PRESERVE); ast_clearflag(ret_type, AST_FLAG_PRESERVE); ast_clearflag(body, AST_FLAG_PRESERVE); const char* fn_name = "apply"; if(ast_id(name) == TK_ID) fn_name = ast_name(name); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE ANNOTATE(annotation) TREE(receiver_cap) ID(fn_name) TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE)); // Doc string ast_list_append(members, &last_member, apply); ast_setflag(members, AST_FLAG_PRESERVE); printbuf_t* buf = printbuf_new(); printbuf(buf, bare ? "@{(" : "{("); bool first = true; for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { if(first) first = false; else printbuf(buf, ", "); printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1))); } printbuf(buf, ")"); if(ast_id(ret_type) != TK_NONE) printbuf(buf, ": %s", ast_print_type(ret_type)); if(ast_id(raises) != TK_NONE) printbuf(buf, " ?"); printbuf(buf, "}"); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, DATA(stringtab(buf->m)) TREE(obj_cap) NONE // Provides list TREE(members))); printbuf_free(buf); if(bare) { BUILD(bare_annotation, *astp, NODE(TK_ANNOTATION, ID("ponyint_bare"))); // Record the syntax pass as done to avoid the error about internal // annotations. ast_pass_record(bare_annotation, PASS_SYNTAX); ast_setannotation(*astp, bare_annotation); } // Catch up passes if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; return ast_passes_subtree(astp, opt, PASS_EXPR); }
// Find the declaration for the specified FFI name that is valid for the given // build config. // The declaration found is stored in the given decl info argument. // There must be exactly one valid declaration. // If a declaration is already specified in the given decl info this must be // the same as the one found. // All other cases are errors, which will be reported by this function. // Returns: true on success, false on failure. static bool find_decl_for_config(ast_t* call, ast_t* package, const char* ffi_name, buildflagset_t* config, ffi_decl_t* decl_info, pass_opt_t* opt) { pony_assert(call != NULL); pony_assert(package != NULL); pony_assert(ffi_name != NULL); pony_assert(config != NULL); pony_assert(decl_info != NULL); bool had_valid_decl = false; // FFI declarations are package wide, so check all modules in package. for(ast_t* m = ast_child(package); m != NULL; m = ast_sibling(m)) { // Find all the FFI declarations in this module. for(ast_t* use = ast_child(m); use != NULL; use = ast_sibling(use)) { if(ast_id(use) == TK_USE) { AST_GET_CHILDREN(use, alias, decl, guard); if(ast_id(decl) == TK_FFIDECL && ffi_name == ast_name(ast_child(decl))) { // We have an FFI declaration for the specified name. if(cond_eval(guard, config, false, opt)) { // This declaration is valid for this config. had_valid_decl = true; if(decl_info->decl != NULL) { // We already have a decalaration, is it the same one? if(decl_info->decl != decl) { ast_error(opt->check.errors, call, "Multiple possible declarations for FFI call"); ast_error_continue(opt->check.errors, decl_info->decl, "This declaration valid for config: %s", decl_info->config); ast_error_continue(opt->check.errors, decl, "This declaration valid for config: %s", buildflagset_print(config)); return false; } } else { // Store the declaration found. // We store the config string incase we need it for error // messages later. We stringtab it because the version we are // given is in a temporary buffer. decl_info->decl = decl; decl_info->config = stringtab(buildflagset_print(config)); } } } } } } if(!had_valid_decl) { ast_error(opt->check.errors, call, "No FFI declaration found for '%s' in config: %s", ffi_name, buildflagset_print(config)); return false; } return true; }
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; }
// Combine the given delegated method with the existing one, if any, in the // given entity. // The trait_ref is the entry in the delegates list that causes this method // inclusion. Needed for error reporting. // Returns true on success, false on failure in which case an error will have // been reported. static bool delegated_method(ast_t* entity, ast_t* method, ast_t* field, ast_t* trait_ref, pass_opt_t* opt) { assert(entity != NULL); assert(method != NULL); assert(is_method(method)); assert(field != NULL); assert(trait_ref != NULL); if(ast_id(method) == TK_NEW) // Don't delegate constructors. return true; // Check for existing method of the same name. const char* name = ast_name(ast_childidx(method, 1)); assert(name != NULL); ast_t* existing = find_method(entity, name); if(existing != NULL) { // We already have a method with this name. method_t* info = (method_t*)ast_data(existing); assert(info != NULL); // Nothing new to add. if(info->local_define || info->delegated_field == field) return true; assert(info->trait_ref != NULL); ast_error(opt->check.errors, trait_ref, "clashing delegates for method %s, local disambiguation required", name); ast_error_continue(opt->check.errors, trait_ref, "field %s delegates to %s here", ast_name(ast_child(field)), name); ast_error_continue(opt->check.errors, info->trait_ref, "field %s delegates to %s here", ast_name(ast_child(info->delegated_field)), name); info->failed = true; info->delegated_field = NULL; return false; } // This is a new method, reify and add it. ast_t* reified = reify_provides_type(method, trait_ref, opt); // Reification error, already reported if(reified == NULL) return false; ast_t* new_method = add_method(entity, trait_ref, reified, "delegate", opt); if(new_method == NULL) { ast_free_unattached(reified); return false; } // Convert the newly added method into a delegation redirection. make_delegation(new_method, field, trait_ref, entity); return true; }
// Combine the given inherited method with the existing one, if any, in the // given entity. // The provided method must already be reified. // The trait_ref is the entry in the provides list that causes this method // inclusion. Needed for error reporting. // Returns true on success, false on failure in which case an error will have // been reported. static bool add_method_from_trait(ast_t* entity, ast_t* method, ast_t* trait_ref, pass_opt_t* opt) { assert(entity != NULL); assert(method != NULL); assert(trait_ref != NULL); AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, method_body); const char* method_name = ast_name(id); ast_t* existing_method = find_method(entity, method_name); if(existing_method == NULL) { // We don't have a method yet with this name, add the one from this trait. ast_t* m = add_method(entity, trait_ref, method, "provided", opt); if(m == NULL) return false; if(ast_id(ast_childidx(m, 6)) != TK_NONE) ast_visit(&m, rescope, NULL, opt, PASS_ALL); return true; } // A method with this name already exists. method_t* info = (method_t*)ast_data(existing_method); assert(info != NULL); // Method has already caused an error, do nothing. if(info->failed) return false; if(info->local_define || info->delegated_field != NULL) return true; // Existing method is also provided, signatures must match exactly. if(!compare_signatures(existing_method, method)) { assert(info->trait_ref != NULL); ast_error(opt->check.errors, trait_ref, "clashing definitions for method '%s' provided, local disambiguation " "required", method_name); ast_error_continue(opt->check.errors, trait_ref, "provided here, type: %s", ast_print_type(method)); ast_error_continue(opt->check.errors, info->trait_ref, "and here, type: %s", ast_print_type(existing_method)); info->failed = true; return false; } // Resolve bodies, if any. ast_t* existing_body = ast_childidx(existing_method, 6); bool multiple_bodies = (info->body_donor != NULL) && (ast_id(method_body) != TK_NONE) && (info->body_donor != (ast_t*)ast_data(method)); if(multiple_bodies || ast_checkflag(existing_method, AST_FLAG_AMBIGUOUS) || ast_checkflag(method, AST_FLAG_AMBIGUOUS)) { // This method body ambiguous, which is not necessarily an error. ast_setflag(existing_method, AST_FLAG_AMBIGUOUS); if(ast_id(existing_body) != TK_NONE) // Ditch existing body. ast_replace(&existing_body, ast_from(existing_method, TK_NONE)); info->body_donor = NULL; return true; } // No new body to resolve. if((ast_id(method_body) == TK_NONE) || (info->body_donor == (ast_t*)ast_data(method))) return true; // Trait provides default body. Use it and patch up symbol tables. assert(ast_id(existing_body) == TK_NONE); ast_replace(&existing_body, method_body); ast_visit(&method_body, rescope, NULL, opt, PASS_ALL); info->body_donor = (ast_t*)ast_data(method); info->trait_ref = trait_ref; return true; }
// Add a new method to the given entity, based on the specified method from // the specified type. // The trait_ref is the entry in the provides / delegates list that causes this // method inclusion. Needed for error reporting. // The basis_method is the reified method in the trait to add. // The adjective parameter is used for error reporting and should be "provided" // or similar. // Return the newly added method or NULL on error. static ast_t* add_method(ast_t* entity, ast_t* trait_ref, ast_t* basis_method, const char* adjective, pass_opt_t* opt) { assert(entity != NULL); assert(trait_ref != NULL); assert(basis_method != NULL); assert(adjective != NULL); const char* name = ast_name(ast_childidx(basis_method, 1)); // Check behaviour compatability. if(ast_id(basis_method) == TK_BE) { switch(ast_id(entity)) { case TK_PRIMITIVE: ast_error(opt->check.errors, trait_ref, "cannot add a behaviour (%s) to a primitive", name); return NULL; case TK_STRUCT: ast_error(opt->check.errors, trait_ref, "cannot add a behaviour (%s) to a struct", name); return NULL; case TK_CLASS: ast_error(opt->check.errors, trait_ref, "cannot add a behaviour (%s) to a class", name); return NULL; default: break; } } // Check for existing method of the same name. ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL) { assert(is_field(existing)); // Should already have checked for methods. ast_error(opt->check.errors, trait_ref, "%s method '%s' clashes with field", adjective, name); ast_error_continue(opt->check.errors, basis_method, "method is defined here"); return NULL; } // Check for clash with existing method. ast_t* case_clash = ast_get_case(entity, name, NULL); if(case_clash != NULL) { const char* clash_name = ""; switch(ast_id(case_clash)) { case TK_FUN: case TK_BE: case TK_NEW: clash_name = ast_name(ast_childidx(case_clash, 1)); break; case TK_LET: case TK_VAR: case TK_EMBED: clash_name = ast_name(ast_child(case_clash)); break; default: assert(0); break; } ast_error(opt->check.errors, trait_ref, "%s method '%s' differs only in case from '%s'", adjective, name, clash_name); ast_error_continue(opt->check.errors, basis_method, "clashing method is defined here"); return NULL; } AST_GET_CHILDREN(basis_method, cap, id, typeparams, params, result, can_error, body, doc); // Ignore docstring. if(ast_id(doc) == TK_STRING) { ast_set_name(doc, ""); ast_setid(doc, TK_NONE); } ast_t* local = ast_append(ast_childidx(entity, 4), basis_method); ast_set(entity, name, local, SYM_DEFINED, false); ast_t* body_donor = (ast_t*)ast_data(basis_method); method_t* info = attach_method_t(local); info->trait_ref = trait_ref; if(ast_id(body) != TK_NONE) info->body_donor = body_donor; return local; }
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; }
bool expr_case(pass_opt_t* opt, ast_t* ast) { assert(opt != NULL); assert(ast_id(ast) == TK_CASE); AST_GET_CHILDREN(ast, pattern, guard, body); if((ast_id(pattern) == TK_NONE) && (ast_id(guard) == TK_NONE)) { ast_error(opt->check.errors, ast, "can't have a case with no conditions, use an else clause"); return false; } ast_t* cases = ast_parent(ast); ast_t* match = ast_parent(cases); ast_t* match_expr = ast_child(match); ast_t* match_type = ast_type(match_expr); if(is_control_type(match_type) || is_typecheck_error(match_type)) return false; if(!infer_pattern_type(pattern, match_type, opt)) return false; if(!is_valid_pattern(opt, pattern)) return false; ast_t* operand_type = alias(match_type); ast_t* pattern_type = ast_type(pattern); bool ok = true; switch(is_matchtype(operand_type, pattern_type, opt)) { case MATCHTYPE_ACCEPT: break; case MATCHTYPE_REJECT: ast_error(opt->check.errors, pattern, "this pattern can never match"); ast_error_continue(opt->check.errors, match_type, "match type: %s", ast_print_type(operand_type)); ast_error_continue(opt->check.errors, pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; case MATCHTYPE_DENY: ast_error(opt->check.errors, pattern, "this capture violates capabilities"); ast_error_continue(opt->check.errors, match_type, "match type: %s", ast_print_type(operand_type)); ast_error_continue(opt->check.errors, pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; } if(ast_id(guard) != TK_NONE) { ast_t* guard_type = ast_type(guard); if(is_typecheck_error(guard_type)) { ok = false; } else if(!is_bool(guard_type)) { ast_error(opt->check.errors, guard, "guard must be a boolean expression"); ok = false; } } ast_free_unattached(operand_type); ast_inheritflags(ast); return ok; }