static bool auto_recover_call(ast_t* ast, ast_t* receiver_type, ast_t* positional, ast_t* result) { switch(ast_id(ast)) { case TK_FUNREF: case TK_FUNAPP: case TK_FUNCHAIN: break; default: assert(0); break; } // We can recover the receiver (ie not alias the receiver type) if all // arguments are safe and the result is either safe or unused. // The result of a chained method is always unused. ast_t* call = ast_parent(ast); if(is_result_needed(call) && !safe_to_autorecover(receiver_type, result)) return false; ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) != TK_NONE) { ast_t* arg_type = ast_type(arg); if(is_typecheck_error(arg_type)) return false; ast_t* a_type = alias(arg_type); bool ok = safe_to_autorecover(receiver_type, a_type); ast_free_unattached(a_type); if(!ok) return false; } arg = ast_sibling(arg); } return true; }
static matchtype_t could_subtype_arrow(ast_t* sub, ast_t* super) { if(ast_id(super) == TK_ARROW) { // If we have the same viewpoint, check the right side. AST_GET_CHILDREN(sub, sub_left, sub_right); AST_GET_CHILDREN(super, super_left, super_right); bool check = false; switch(ast_id(sub_left)) { case TK_THISTYPE: switch(ast_id(super_left)) { case TK_THISTYPE: case TK_BOXTYPE: check = true; break; default: {} } break; case TK_BOXTYPE: check = ast_id(super_left) == TK_BOXTYPE; break; default: check = is_eqtype(sub_left, super_left); break; } if(check) return could_subtype(sub_right, super_right); } // Check the lower bounds. ast_t* lower = viewpoint_lower(sub); matchtype_t ok = could_subtype(super, lower); if(lower != sub) ast_free_unattached(lower); return ok; }
static void reach_type_free(reach_type_t* t) { ast_free(t->ast); reach_method_names_destroy(&t->methods); reach_type_cache_destroy(&t->subtypes); if(t->field_count > 0) { for(uint32_t i = 0; i < t->field_count; i++) ast_free_unattached(t->fields[i].ast); free(t->fields); t->field_count = 0; t->fields = NULL; } POOL_FREE(reach_type_t, t); }
static void flatten_typeexpr_element(ast_t* type, ast_t* elem, token_id id) { if(ast_id(elem) != id) { ast_append(type, elem); return; } ast_t* child = ast_child(elem); while(child != NULL) { ast_append(type, child); child = ast_sibling(child); } ast_free_unattached(elem); }
// Process the methods provided to the given entity from all traits in its // provides list. static bool provided_methods(ast_t* entity, pass_opt_t* opt) { assert(entity != NULL); ast_t* provides = ast_childidx(entity, 3); bool r = true; // Run through our provides list for(ast_t* trait_ref = ast_child(provides); trait_ref != NULL; trait_ref = ast_sibling(trait_ref)) { ast_t* trait = (ast_t*)ast_data(trait_ref); assert(trait != NULL); if(!trait_entity(trait, opt)) return false; ast_t* members = ast_childidx(trait, 4); // Run through the methods of each provided type. for(ast_t* method = ast_child(members); method != NULL; method = ast_sibling(method)) { assert(is_method(method)); ast_t* reified = reify_provides_type(method, trait_ref, opt); if(reified == NULL) { // Reification error, already reported. r = false; } else { if(!add_method_from_trait(entity, reified, trait_ref, opt)) r = false; ast_free_unattached(reified); } } } return r; }
static bool safe_field_write(token_id cap, ast_t* type) { switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { // Safe to write if every component is safe to write. ast_t* child = ast_child(type); while(child != NULL) { if(!safe_field_write(cap, child)) return false; child = ast_sibling(child); } return true; } case TK_ARROW: { ast_t* upper = viewpoint_upper(type); bool ok = safe_field_write(cap, upper); if(upper != type) ast_free_unattached(upper); return ok; } case TK_NOMINAL: case TK_TYPEPARAMREF: return cap_safetowrite(cap, cap_single(type)); default: {} } assert(0); return false; }
static ast_t* type_typeexpr(token_id t, ast_t* l_type, ast_t* r_type) { bool is_union = t == TK_UNIONTYPE; if(l_type == NULL) return r_type; if(r_type == NULL) return l_type; if(is_subtype(l_type, r_type)) { if(is_union) return r_type; else return l_type; } if(is_subtype(r_type, l_type)) { if(is_union) return l_type; else return r_type; } ast_t* type = ast_from(l_type, t); append_to_typeexpr(type, l_type, is_union); append_to_typeexpr(type, r_type, is_union); // If there's only one element, remove the type expression node. ast_t* child = ast_child(type); if(ast_sibling(child) == NULL) { child = ast_dup(child); ast_free_unattached(type); type = child; } return type; }
static reach_method_name_t* add_method_name(reach_type_t* t, const char* name) { reach_method_name_t* n = reach_method_name(t, name); if(n == NULL) { n = POOL_ALLOC(reach_method_name_t); n->name = name; reach_methods_init(&n->r_methods, 0); reach_mangled_init(&n->r_mangled, 0); reach_method_names_put(&t->methods, n); ast_t* fun = lookup(NULL, NULL, t->ast, name); n->id = ast_id(fun); n->cap = ast_id(ast_child(fun)); ast_free_unattached(fun); } return n; }
static matchtype_t is_arrow_match_x(ast_t* operand, ast_t* pattern, errorframe_t* errorf, bool report_reject, pass_opt_t* opt) { // upperbound(this->T1) match T2 // --- // (this->T1) match T2 // lowerbound(T1->T2) match T3 // --- // (T1->T2) match T3 ast_t* operand_view; AST_GET_CHILDREN(operand, left, right); if(ast_id(left) == TK_THISTYPE) operand_view = viewpoint_upper(operand); else operand_view = viewpoint_lower(operand); if(operand_view == NULL) { if(errorf != NULL) { // this->X always has an upper bound. pony_assert(ast_id(left) != TK_THISTYPE); ast_error_frame(errorf, pattern, "matching %s with %s could violate capabilities: " "the match type has no lower bounds", ast_print_type(operand), ast_print_type(pattern)); } return MATCHTYPE_DENY; } matchtype_t ok = is_x_match_x(operand_view, pattern, errorf, report_reject, opt); ast_free_unattached(operand_view); return ok; }
// Coerce a literal control block to be the specified target type static bool coerce_control_block(ast_t** astp, ast_t* target_type, lit_chain_t* chain, pass_opt_t* options, bool report_errors) { assert(astp != NULL); ast_t* literal_expr = *astp; assert(literal_expr != NULL); ast_t* lit_type = ast_type(literal_expr); assert(lit_type != NULL); assert(ast_id(lit_type) == TK_LITERAL); ast_t* block_type = ast_type(lit_type); for(ast_t* p = ast_child(lit_type); p != NULL; p = ast_sibling(p)) { assert(ast_id(p) == TK_LITERALBRANCH); ast_t* branch = (ast_t*)ast_data(p); assert(branch != NULL); if(!coerce_literal_to_type(&branch, target_type, chain, options, report_errors)) { ast_free_unattached(block_type); return false; } block_type = type_union(block_type, ast_type(branch)); } if(is_typecheck_error(block_type)) return false; // block_type may be a sub-tree of the current type of literal_expr. // This means we must copy it before setting it as the type since ast_settype // will first free the existing type of literal_expr, which may include // block_type. if(ast_parent(block_type) != NULL) block_type = ast_dup(block_type); ast_settype(literal_expr, block_type); return true; }
// Tidy up the method_t structures in the given type static void tidy_up(ast_t* ast) { assert(ast != NULL); ast_t* members = ast_childidx(ast, 4); assert(members != NULL); for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { if(is_method(p)) { method_t* info = (method_t*)ast_data(p); assert(info != NULL); ast_t* body_donor = info->body_donor; ast_free_unattached(info->reified_default); POOL_FREE(method_t, info); ast_setdata(p, body_donor); } } }
LLVMValueRef gen_fieldload(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, left, right); LLVMValueRef field = gen_fieldptr(c, ast); if(field == NULL) return NULL; deferred_reification_t* reify = c->frame->reify; ast_t* type = deferred_reify(reify, ast_type(right), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); ast_free_unattached(type); compile_type_t* c_t = (compile_type_t*)t->c_type; field = LLVMBuildLoad(c->builder, field, ""); return gen_assign_cast(c, c_t->use_type, field, t->ast_cap); }
LLVMValueRef gen_int(compile_t* c, ast_t* ast) { ast_t* type = deferred_reify(c->frame->reify, ast_type(ast), c->opt); reach_type_t* t = reach_type(c->reach, type); ast_free_unattached(type); compile_type_t* c_t = (compile_type_t*)t->c_type; lexint_t* value = ast_int(ast); LLVMValueRef vlow = LLVMConstInt(c->i128, value->low, false); LLVMValueRef vhigh = LLVMConstInt(c->i128, value->high, false); LLVMValueRef shift = LLVMConstInt(c->i128, 64, false); vhigh = LLVMConstShl(vhigh, shift); vhigh = LLVMConstAdd(vhigh, vlow); if(c_t->primitive == c->i128) return vhigh; if((c_t->primitive == c->f32) || (c_t->primitive == c->f64)) return LLVMConstUIToFP(vhigh, c_t->primitive); return LLVMConstTrunc(vhigh, c_t->primitive); }
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name, reach_type_t* t, ast_t* args, bool intrinsic) { ast_t* last_arg = ast_childlast(args); if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS)) return declare_ffi_vararg(c, f_name, t); int count = (int)ast_childcount(args); size_t buf_size = count * sizeof(LLVMTypeRef); LLVMTypeRef* f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); count = 0; ast_t* arg = ast_child(args); deferred_reification_t* reify = c->frame->reify; while(arg != NULL) { ast_t* p_type = ast_type(arg); if(p_type == NULL) p_type = ast_childidx(arg, 1); p_type = deferred_reify(reify, p_type, c->opt); reach_type_t* pt = reach_type(c->reach, p_type); pony_assert(pt != NULL); f_params[count++] = ((compile_type_t*)pt->c_type)->use_type; ast_free_unattached(p_type); arg = ast_sibling(arg); } LLVMTypeRef r_type = ffi_return_type(c, t, intrinsic); LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false); LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type); ponyint_pool_free_size(buf_size, f_params); return func; }
static void add_special(reach_t* r, reach_type_t* t, ast_t* type, const char* special, pass_opt_t* opt) { special = stringtab(special); ast_t* find = lookup_try(NULL, NULL, type, special); if(find != NULL) { switch(ast_id(find)) { case TK_NEW: case TK_FUN: case TK_BE: { reachable_method(r, t->ast, special, NULL, opt); ast_free_unattached(find); break; } default: {} } } }
static bool is_arrow_sub_nominal(ast_t* sub, ast_t* super, errorframe_t* errors) { // upperbound(T1->T2) <: N k // --- // T1->T2 <: N k ast_t* sub_upper = viewpoint_upper(sub); if(sub_upper == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the subtype has no upper bounds", ast_print_type(sub), ast_print_type(super)); } return false; } bool ok = is_subtype(sub_upper, super, errors); ast_free_unattached(sub_upper); return ok; }
static bool is_nominal_sub_arrow(ast_t* sub, ast_t* super, errorframe_t* errors) { // N k <: lowerbound(T1->T2) // --- // N k <: T1->T2 ast_t* super_lower = viewpoint_lower(super); if(super_lower == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the supertype has no lower bounds", ast_print_type(sub), ast_print_type(super)); } return false; } bool ok = is_subtype(sub, super_lower, errors); ast_free_unattached(super_lower); return ok; }
static bool is_nominal_sub_typeparam(ast_t* sub, ast_t* super, errorframe_t* errors) { // N k <: lowerbound(A k') // --- // N k <: A k' ast_t* super_lower = typeparam_lower(super); if(super_lower == NULL) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: the type parameter has no lower bounds", ast_print_type(sub), ast_print_type(super)); } return false; } bool ok = is_subtype(sub, super_lower, errors); ast_free_unattached(super_lower); return ok; }
static matchtype_t is_entity_match_trait(ast_t* operand, ast_t* pattern) { AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph); AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph); token_id tcap = ast_id(p_cap); token_id teph = ast_id(p_eph); ast_t* r_operand = set_cap_and_ephemeral(operand, tcap, teph); bool provides = is_subtype(r_operand, pattern, false); ast_free_unattached(r_operand); // If the operand doesn't provide the pattern (trait or interface), reject // the match. if(!provides) return MATCHTYPE_REJECT; // If the operand does provide the pattern, but the operand refcap can't // match the pattern refcap, deny the match. if(!is_cap_match_cap(ast_id(o_cap), ast_id(o_eph), tcap, teph)) return MATCHTYPE_DENY; // Otherwise, accept the match. return MATCHTYPE_ACCEPT; }
static bool type_access(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; // Left is a typeref, right is an id. ast_t* left = ast_child(ast); ast_t* right = ast_sibling(left); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; assert(ast_id(left) == TK_TYPEREF); assert(ast_id(right) == TK_ID); ast_t* find = lookup(opt, ast, type, ast_name(right)); if(find == NULL) return false; bool ret = true; switch(ast_id(find)) { case TK_TYPEPARAM: ast_error(opt->check.errors, right, "can't look up a typeparam on a type"); ret = false; break; case TK_NEW: ret = method_access(opt, ast, find); break; case TK_FVAR: case TK_FLET: case TK_EMBED: case TK_BE: case TK_FUN: { // Make this a lookup on a default constructed object. ast_free_unattached(find); if(!strcmp(ast_name(right), "create")) { ast_error(opt->check.errors, right, "create is not a constructor on this type"); return false; } ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(left, dot); ast_add(dot, left); ast_t* call = ast_from(ast, TK_CALL); ast_swap(dot, call); ast_add(call, dot); // the LHS goes at the end, not the beginning ast_add(call, ast_from(ast, TK_NONE)); // named ast_add(call, ast_from(ast, TK_NONE)); // positional if(!expr_dot(opt, &dot)) return false; if(!expr_call(opt, &call)) return false; return expr_dot(opt, astp); } default: assert(0); ret = false; break; } ast_free_unattached(find); return ret; }
bool expr_typeref(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEREF); ast_t* type = ast_type(ast); if(is_typecheck_error(type)) return false; switch(ast_id(ast_parent(ast))) { case TK_QUALIFY: // Doesn't have to be valid yet. break; case TK_DOT: // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } break; case TK_CALL: { // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Transform to a default constructor. ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(ast, dot); *astp = dot; ast_add(dot, ast); if(!expr_dot(opt, astp)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } ast_t* ast = *astp; // If the default constructor has no parameters, transform to an apply // call. if((ast_id(ast) == TK_NEWREF) || (ast_id(ast) == TK_NEWBEREF)) { type = ast_type(ast); if(is_typecheck_error(type)) return false; assert(ast_id(type) == TK_FUNTYPE); AST_GET_CHILDREN(type, cap, typeparams, params, result); if(ast_id(params) == TK_NONE) { // Add a call node. ast_t* call = ast_from(ast, TK_CALL); ast_add(call, ast_from(call, TK_NONE)); // Named ast_add(call, ast_from(call, TK_NONE)); // Positional ast_swap(ast, call); ast_append(call, ast); if(!expr_call(opt, &call)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Add a dot node. ast_t* apply = ast_from(call, TK_DOT); ast_add(apply, ast_from_string(call, "apply")); ast_swap(call, apply); ast_add(apply, call); if(!expr_dot(opt, &apply)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } } } return true; } default: { // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Transform to a default constructor. ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(ast, dot); ast_add(dot, ast); // Call the default constructor with no arguments. ast_t* call = ast_from(ast, TK_CALL); ast_swap(dot, call); ast_add(call, dot); // Receiver comes last. ast_add(call, ast_from(ast, TK_NONE)); // Named args. ast_add(call, ast_from(ast, TK_NONE)); // Positional args. *astp = call; if(!expr_dot(opt, &dot)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } if(!expr_call(opt, astp)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } break; } } return true; }
static bool is_fun_sub_fun(ast_t* sub, ast_t* super, ast_t* isub, ast_t* isuper) { token_id tsub = ast_id(sub); token_id tsuper = ast_id(super); switch(tsub) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } switch(tsuper) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } // A constructor can only be a subtype of a constructor. if(((tsub == TK_NEW) || (tsuper == TK_NEW)) && (tsub != tsuper)) return false; AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params); // Must have the same name. if(ast_name(sub_id) != ast_name(super_id)) return false; // Must have the same number of type parameters and parameters. if((ast_childcount(sub_typeparams) != ast_childcount(super_typeparams)) || (ast_childcount(sub_params) != ast_childcount(super_params))) return false; ast_t* r_sub = sub; if(ast_id(super_typeparams) != TK_NONE) { // Reify sub with the type parameters of super. BUILD(typeargs, super_typeparams, NODE(TK_TYPEARGS)); ast_t* super_typeparam = ast_child(super_typeparams); while(super_typeparam != NULL) { AST_GET_CHILDREN(super_typeparam, super_id, super_constraint); token_id cap = cap_from_constraint(super_constraint); BUILD(typearg, super_typeparam, NODE(TK_TYPEPARAMREF, TREE(super_id) NODE(cap) NONE)); ast_t* def = ast_get(super_typeparam, ast_name(super_id), NULL); ast_setdata(typearg, def); ast_append(typeargs, typearg); super_typeparam = ast_sibling(super_typeparam); } r_sub = reify(sub, sub, sub_typeparams, typeargs); ast_free_unattached(typeargs); } bool ok = is_reified_fun_sub_fun(r_sub, super, isub, isuper); if(r_sub != sub) ast_free_unattached(r_sub); return ok; }
static bool gen_field_init(compile_t* c, gentype_t* g) { LLVMValueRef this_ptr = LLVMGetParam(codegen_fun(c), 0); ast_t* def = (ast_t*)ast_data(g->ast); ast_t* members = ast_childidx(def, 4); ast_t* member = ast_child(members); // Struct index of the current field. int index = 1; if(ast_id(def) == TK_ACTOR) index++; // Iterate through all fields. while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: { // Skip this field if it has no initialiser. AST_GET_CHILDREN(member, id, type, body); if(ast_id(body) != TK_NONE) { // Reify the initialiser. ast_t* this_type = set_cap_and_ephemeral(g->ast, TK_REF, TK_NONE); ast_t* var = lookup(NULL, NULL, this_type, ast_name(id)); ast_free_unattached(this_type); assert(var != NULL); body = ast_childidx(var, 2); // Get the field pointer. dwarf_location(&c->dwarf, body); LLVMValueRef l_value = LLVMBuildStructGEP(c->builder, this_ptr, index, ""); // Cast the initialiser to the field type. LLVMValueRef r_value = gen_expr(c, body); if(r_value == NULL) return false; LLVMTypeRef l_type = LLVMGetElementType(LLVMTypeOf(l_value)); LLVMValueRef cast_value = gen_assign_cast(c, l_type, r_value, ast_type(body)); if(cast_value == NULL) return false; // Store the result. LLVMBuildStore(c->builder, cast_value, l_value); } index++; break; } default: {} } member = ast_sibling(member); } return true; }
bool expr_fieldref(pass_opt_t* opt, ast_t* ast, ast_t* find, token_id tid) { AST_GET_CHILDREN(ast, left, right); ast_t* l_type = ast_type(left); if(is_typecheck_error(l_type)) return false; AST_GET_CHILDREN(find, id, f_type, init); // Viewpoint adapted type of the field. ast_t* type = viewpoint_type(l_type, f_type); if(ast_id(type) == TK_ARROW) { ast_t* upper = viewpoint_upper(type); if(upper == NULL) { ast_error(opt->check.errors, ast, "can't read a field through %s", ast_print_type(l_type)); return false; } ast_free_unattached(upper); } // In a recover expression, we can access obj.field if field is sendable // and not being assigned to, even if obj isn't sendable. typecheck_t* t = &opt->check; if(t->frame->recover != NULL) { if(!sendable(type)) { if(!sendable(l_type)) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "can't access field of non-sendable " "object inside of a recover expression"); ast_error_frame(&frame, find, "this would be possible if the field was " "sendable"); errorframe_report(&frame, opt->check.errors); return false; } } else { ast_t* parent = ast_parent(ast); ast_t* current = ast; while(ast_id(parent) != TK_RECOVER && ast_id(parent) != TK_ASSIGN) { current = parent; parent = ast_parent(parent); } if(ast_id(parent) == TK_ASSIGN && ast_child(parent) != current) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "can't access field of non-sendable " "object inside of a recover expression"); ast_error_frame(&frame, parent, "this would be possible if the field " "wasn't assigned to"); errorframe_report(&frame, opt->check.errors); return false; } } } // Set the unadapted field type. ast_settype(right, f_type); // Set the type so that it isn't free'd as unattached. ast_setid(ast, tid); ast_settype(ast, type); if(ast_id(left) == TK_THIS) { // Handle symbol status if the left side is 'this'. const char* name = ast_name(id); sym_status_t status; ast_get(ast, name, &status); if(!valid_reference(opt, ast, type, status)) return false; } return true; }
static void trace_dynamic_tuple(compile_t* c, LLVMValueRef ctx, LLVMValueRef ptr, LLVMValueRef desc, ast_t* type, ast_t* orig, ast_t* tuple) { // Build a "don't care" type of our cardinality. size_t cardinality = ast_childcount(type); ast_t* dontcare = ast_from(type, TK_TUPLETYPE); for(size_t i = 0; i < cardinality; i++) ast_append(dontcare, ast_from(type, TK_DONTCARE)); // Replace our type in the tuple type with the "don't care" type. bool in_tuple = (tuple != NULL); if(in_tuple) ast_swap(type, dontcare); else tuple = dontcare; // If the original type is a subtype of the test type, then we are always // the correct cardinality. Otherwise, we need to dynamically check // cardinality. LLVMBasicBlockRef is_true = codegen_block(c, ""); LLVMBasicBlockRef is_false = codegen_block(c, ""); if(!is_subtype(orig, tuple, NULL)) { LLVMValueRef dynamic_count = gendesc_fieldcount(c, desc); LLVMValueRef static_count = LLVMConstInt(c->i32, cardinality, false); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntEQ, static_count, dynamic_count, ""); // Skip if not the right cardinality. LLVMBuildCondBr(c->builder, test, is_true, is_false); } else { LLVMBuildBr(c->builder, is_true); } LLVMPositionBuilderAtEnd(c->builder, is_true); size_t index = 0; ast_t* child = ast_child(type); ast_t* dc_child = ast_child(dontcare); while(child != NULL) { switch(trace_type(child)) { case TRACE_PRIMITIVE: // Skip this element. break; case TRACE_ACTOR: case TRACE_KNOWN: case TRACE_UNKNOWN: case TRACE_KNOWN_VAL: case TRACE_UNKNOWN_VAL: case TRACE_TAG: case TRACE_TAG_OR_ACTOR: case TRACE_DYNAMIC: { // If we are (A, B), turn (_, _) into (A, _). ast_t* swap = ast_dup(child); ast_swap(dc_child, swap); // Create a next block. LLVMBasicBlockRef next_block = codegen_block(c, ""); // Load the object from the tuple field. LLVMValueRef field_info = gendesc_fieldinfo(c, desc, index); LLVMValueRef object = gendesc_fieldload(c, ptr, field_info); // Trace dynamic, even if the tuple thinks the field isn't dynamic. trace_dynamic(c, ctx, object, swap, orig, tuple, next_block); // Continue into the next block. LLVMBuildBr(c->builder, next_block); LLVMPositionBuilderAtEnd(c->builder, next_block); // Restore (A, _) to (_, _). ast_swap(swap, dc_child); ast_free_unattached(swap); break; } case TRACE_TUPLE: { // If we are (A, B), turn (_, _) into (A, _). ast_t* swap = ast_dup(child); ast_swap(dc_child, swap); // Get a pointer to the unboxed tuple and it's descriptor. LLVMValueRef field_info = gendesc_fieldinfo(c, desc, index); LLVMValueRef field_ptr = gendesc_fieldptr(c, ptr, field_info); LLVMValueRef field_desc = gendesc_fielddesc(c, field_info); // Trace the tuple dynamically. trace_dynamic_tuple(c, ctx, field_ptr, field_desc, swap, orig, tuple); // Restore (A, _) to (_, _). ast_swap(swap, dc_child); ast_free_unattached(swap); break; } default: {} } index++; child = ast_sibling(child); dc_child = ast_sibling(dc_child); } // Restore the tuple type. if(in_tuple) ast_swap(dontcare, type); ast_free_unattached(dontcare); // Continue with other possible tracings. LLVMBuildBr(c->builder, is_false); LLVMPositionBuilderAtEnd(c->builder, is_false); }
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; }
static bool check_receiver_cap(pass_opt_t* opt, ast_t* ast, bool incomplete) { 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); if(incomplete && (ast_id(receiver) == TK_THIS)) { // If 'this' is incomplete and the arg is 'this', change the type to tag. ast_t* tag_type = set_cap_and_ephemeral(a_type, TK_TAG, TK_NONE); if(a_type != r_type) ast_free_unattached(a_type); a_type = tag_type; } else { incomplete = 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, 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"); } if(incomplete && is_subtype(r_type, t_type, NULL, opt)) { ast_error_frame(&frame, ast, "this would be possible if all the fields of 'this' were assigned to " "at this point"); } 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; }
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; }
static ast_t* lookup_base(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { switch(ast_id(type)) { case TK_UNIONTYPE: { ast_t* child = ast_child(type); ast_t* result = NULL; bool ok = true; while(child != NULL) { ast_t* r = lookup_base(opt, from, child, child, name, errors); if(r == NULL) { // All possible types in the union must have this. if(errors) { ast_error(from, "couldn't find %s in %s", name, ast_print_type(child)); } ok = false; } else { switch(ast_id(r)) { case TK_FVAR: case TK_FLET: case TK_EMBED: if(errors) { ast_error(from, "can't lookup field %s in %s in a union type", name, ast_print_type(child)); } ok = false; break; default: if(result == NULL) { // If we don't have a result yet, use this one. result = r; } else if(!is_subtype(r, result, false)) { if(is_subtype(result, r, false)) { // Use the supertype function. Require the most specific // arguments and return the least specific result. // TODO: union the signatures, to handle arg names and // default arguments. ast_free_unattached(result); result = r; } else { if(errors) { ast_error(from, "a member of the union type has an incompatible method " "signature"); ast_error(result, "first implementation is here"); ast_error(r, "second implementation is here"); } ast_free_unattached(r); ok = false; } } break; } } child = ast_sibling(child); } if(!ok) { ast_free_unattached(result); result = NULL; } return result; } case TK_ISECTTYPE: { ast_t* child = ast_child(type); ast_t* result = NULL; bool ok = true; while(child != NULL) { ast_t* r = lookup_base(opt, from, child, child, name, false); if(r != NULL) { switch(ast_id(r)) { case TK_FVAR: case TK_FLET: case TK_EMBED: // Ignore fields. break; default: if(result == NULL) { // If we don't have a result yet, use this one. result = r; } else if(!is_subtype(result, r, false)) { if(is_subtype(r, result, false)) { // Use the subtype function. Require the least specific // arguments and return the most specific result. ast_free_unattached(result); result = r; } // TODO: isect the signatures, to handle arg names and // default arguments. This is done even when the functions have // no subtype relationship. } break; } } child = ast_sibling(child); } if(errors && (result == NULL)) ast_error(from, "couldn't find '%s'", name); if(!ok) { ast_free_unattached(result); result = NULL; } return result; } case TK_TUPLETYPE: if(errors) ast_error(from, "can't lookup by name on a tuple"); return NULL; case TK_NOMINAL: return lookup_nominal(opt, from, orig, type, name, errors); case TK_ARROW: return lookup_base(opt, from, orig, ast_childidx(type, 1), name, errors); case TK_TYPEPARAMREF: return lookup_typeparam(opt, from, orig, type, name, errors); case TK_FUNTYPE: if(errors) ast_error(from, "can't lookup by name on a function type"); return NULL; case TK_INFERTYPE: case TK_ERRORTYPE: // Can only happen due to a local inference fail earlier return NULL; default: {} } assert(0); return NULL; }
static void add_fields(reach_t* r, reach_type_t* t, pass_opt_t* opt) { ast_t* def = (ast_t*)ast_data(t->ast); ast_t* typeargs = ast_childidx(t->ast, 2); ast_t* typeparams = ast_childidx(def, 1); ast_t* members = ast_childidx(def, 4); ast_t* member = ast_child(members); while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: case TK_EMBED: { t->field_count++; break; } default: {} } member = ast_sibling(member); } if(t->field_count == 0) return; t->fields = (reach_field_t*)calloc(t->field_count, sizeof(reach_field_t)); member = ast_child(members); size_t index = 0; while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: case TK_EMBED: { ast_t* r_member = lookup(NULL, NULL, t->ast, ast_name(ast_child(member))); assert(r_member != NULL); AST_GET_CHILDREN(r_member, name, type, init); t->fields[index].embed = ast_id(member) == TK_EMBED; t->fields[index].ast = reify(ast_type(member), typeparams, typeargs, opt, true); ast_setpos(t->fields[index].ast, NULL, ast_line(name), ast_pos(name)); t->fields[index].type = add_type(r, type, opt); if(r_member != member) ast_free_unattached(r_member); index++; break; } default: {} } member = ast_sibling(member); } }