static void reachable_expr(reach_t* r, ast_t* ast, pass_opt_t* opt) { // If this is a method call, mark the method as reachable. switch(ast_id(ast)) { case TK_TRUE: case TK_FALSE: case TK_INT: case TK_FLOAT: case TK_STRING: { ast_t* type = ast_type(ast); if(type != NULL) reachable_method(r, type, stringtab("create"), NULL, opt); break; } case TK_LET: case TK_VAR: case TK_TUPLE: { ast_t* type = ast_type(ast); add_type(r, type, opt); break; } case TK_CASE: { AST_GET_CHILDREN(ast, pattern, guard, body); reachable_pattern(r, pattern, opt); reachable_expr(r, guard, opt); reachable_expr(r, body, opt); break; } case TK_CALL: reachable_call(r, ast, opt); break; case TK_FFICALL: reachable_ffi(r, ast, opt); break; case TK_ADDRESS: reachable_addressof(r, ast, opt); break; case TK_IF: { AST_GET_CHILDREN(ast, cond, then_clause, else_clause); assert(ast_id(cond) == TK_SEQ); cond = ast_child(cond); ast_t* type = ast_type(ast); if(is_result_needed(ast) && !is_control_type(type)) add_type(r, type, opt); if(ast_sibling(cond) == NULL) { if(ast_id(cond) == TK_TRUE) { reachable_expr(r, then_clause, opt); return; } else if(ast_id(cond) == TK_FALSE) { reachable_expr(r, else_clause, opt); return; } } break; } case TK_MATCH: case TK_WHILE: case TK_REPEAT: case TK_TRY: { ast_t* type = ast_type(ast); if(is_result_needed(ast) && !is_control_type(type)) add_type(r, type, opt); break; } default: {} } // Traverse all child expressions looking for calls. ast_t* child = ast_child(ast); while(child != NULL) { reachable_expr(r, child, opt); child = ast_sibling(child); } }
static bool is_reified_fun_sub_fun(ast_t* sub, ast_t* super, ast_t* isub, ast_t* isuper) { AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params, sub_result, sub_throws); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params, super_result, super_throws); switch(ast_id(sub)) { case TK_NEW: { // Covariant receiver. if(!is_cap_sub_cap(ast_id(sub_cap), TK_NONE, ast_id(super_cap), TK_NONE)) return false; // Covariant result. Don't check this for interfaces, as it produces // an infinite loop. It will be true if the whole interface is provided. if(isuper == NULL) { if(!is_subtype(sub_result, super_result)) return false; // If either result type is a machine word, the other must be as well. if(is_machine_word(sub_result) && !is_machine_word(super_result)) return false; if(is_machine_word(super_result) && !is_machine_word(sub_result)) return false; } break; } case TK_FUN: case TK_BE: { // Contravariant receiver. if(!is_cap_sub_cap(ast_id(super_cap), TK_NONE, ast_id(sub_cap), TK_NONE)) return false; // Covariant result. if(!is_recursive_interface(sub_result, super_result, isub, isuper)) { if(!is_subtype(sub_result, super_result)) return false; // If either result type is a machine word, the other must be as well. if(is_machine_word(sub_result) && !is_machine_word(super_result)) return false; if(is_machine_word(super_result) && !is_machine_word(sub_result)) return false; } break; } default: {} } // Contravariant type parameter constraints. ast_t* sub_typeparam = ast_child(sub_typeparams); ast_t* super_typeparam = ast_child(super_typeparams); while((sub_typeparam != NULL) && (super_typeparam != NULL)) { ast_t* sub_constraint = ast_childidx(sub_typeparam, 1); ast_t* super_constraint = ast_childidx(super_typeparam, 1); if(!is_recursive_interface(super_constraint, sub_constraint, isub, isuper) && !is_subtype(super_constraint, sub_constraint)) return false; sub_typeparam = ast_sibling(sub_typeparam); super_typeparam = ast_sibling(super_typeparam); } // Contravariant parameters. ast_t* sub_param = ast_child(sub_params); ast_t* super_param = ast_child(super_params); while((sub_param != NULL) && (super_param != NULL)) { ast_t* sub_type = ast_childidx(sub_param, 1); ast_t* super_type = ast_childidx(super_param, 1); // If either parameter type is a machine word, the other must be as well. if(is_machine_word(sub_type) && !is_machine_word(super_type)) return false; if(is_machine_word(super_type) && !is_machine_word(sub_type)) return false; // Contravariant: the super type must be a subtype of the sub type. if(!is_recursive_interface(super_type, sub_type, isub, isuper) && !is_subtype(super_type, sub_type)) return false; sub_param = ast_sibling(sub_param); super_param = ast_sibling(super_param); } if((sub_param != NULL) || (super_param != NULL)) return false; // Covariant throws. if((ast_id(sub_throws) == TK_QUESTION) && (ast_id(super_throws) != TK_QUESTION)) return false; return true; }
static bool check_primitive_init(typecheck_t* t, ast_t* ast) { if(ast_id(t->frame->type) != TK_PRIMITIVE) return true; AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error); if(strcmp(ast_name(id), "_init")) return true; bool ok = true; if(ast_id(ast_childidx(t->frame->type, 1)) != TK_NONE) { ast_error(ast, "a primitive with type parameters cannot have an _init"); ok = false; } if(ast_id(ast) != TK_FUN) { ast_error(ast, "a primitive _init must be a function"); ok = false; } if(ast_id(cap) != TK_BOX) { ast_error(cap, "a primitive _init must be box"); ok = false; } if(ast_id(typeparams) != TK_NONE) { ast_error(typeparams, "a primitive _init must not be polymorphic"); ok = false; } if(ast_childcount(params) != 1) { ast_error(params, "a primitive _init must take a single Env parameter"); ok = false; } ast_t* param = ast_child(params); if(param != NULL) { ast_t* p_type = ast_childidx(param, 1); if(!is_env(p_type)) { ast_error(p_type, "must be of type Env"); ok = false; } } if(!is_none(result)) { ast_error(result, "a primitive _init must return None"); ok = false; } if(ast_id(can_error) != TK_NONE) { ast_error(can_error, "a primitive _init cannot raise an error"); ok = false; } return ok; }
static bool scope_local(typecheck_t* t, ast_t* ast) { // Local resolves to itself ast_t* id = ast_child(ast); return set_scope(t, ast_parent(ast), id, id); }
bool is_actor(ast_t* type) { switch(ast_id(type)) { case TK_TUPLETYPE: return false; case TK_UNIONTYPE: { ast_t* child = ast_child(type); while(child != NULL) { if(!is_actor(child)) return false; child = ast_sibling(child); } return true; } case TK_ISECTTYPE: { ast_t* child = ast_child(type); while(child != NULL) { if(is_actor(child)) return true; child = ast_sibling(child); } return false; } case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(type); switch(ast_id(def)) { case TK_INTERFACE: case TK_TRAIT: case TK_PRIMITIVE: case TK_CLASS: return false; case TK_ACTOR: return true; default: {} } break; } case TK_ARROW: return is_actor(ast_childidx(type, 1)); case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(type); ast_t* constraint = ast_childidx(def, 1); return is_actor(constraint); } default: {} } assert(0); return false; }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { assert(astp != NULL); ast_t* ast = *astp; assert(ast != NULL); AST_GET_CHILDREN(ast, cap, name, t_params, params, captures, ret_type, raises, body); ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = make_capture_field(opt, p); if(field != NULL) ast_list_append(members, &last_member, field); else // 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 TREE(cap) ID(fn_name) TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE // Doc string NONE)); // Guard ast_list_append(members, &last_member, apply); printbuf_t* buf = printbuf_new(); printbuf(buf, "lambda("); 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, " end"); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, DATA(stringtab(buf->m)) NONE NONE // Provides list TREE(members))); printbuf_free(buf); // 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); }
static void reachable_expr(reachable_method_stack_t** s, reachable_types_t* r, uint32_t* next_type_id, ast_t* ast) { // If this is a method call, mark the method as reachable. switch(ast_id(ast)) { case TK_TRUE: case TK_FALSE: case TK_INT: case TK_FLOAT: case TK_STRING: { ast_t* type = ast_type(ast); if(type != NULL) reachable_method(s, r, next_type_id, type, stringtab("create"), NULL); break; } case TK_TUPLE: { ast_t* type = ast_type(ast); add_type(s, r, next_type_id, type); break; } case TK_CASE: { AST_GET_CHILDREN(ast, pattern, guard, body); reachable_pattern(s, r, next_type_id, pattern); reachable_expr(s, r, next_type_id, guard); reachable_expr(s, r, next_type_id, body); break; } case TK_CALL: reachable_call(s, r, next_type_id, ast); break; case TK_FFICALL: reachable_ffi(s, r, next_type_id, ast); break; case TK_ADDRESS: reachable_addressof(s, r, next_type_id, ast); break; case TK_IF: { AST_GET_CHILDREN(ast, cond, then_clause, else_clause); assert(ast_id(cond) == TK_SEQ); cond = ast_child(cond); if(ast_sibling(cond) == NULL) { if(ast_id(cond) == TK_TRUE) { reachable_expr(s, r, next_type_id, then_clause); return; } else if(ast_id(cond) == TK_FALSE) { reachable_expr(s, r, next_type_id, else_clause); return; } } break; } default: {} } // Traverse all child expressions looking for calls. ast_t* child = ast_child(ast); while(child != NULL) { reachable_expr(s, r, next_type_id, child); child = ast_sibling(child); } }
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; }
// Sugar for partial application, which we convert to a lambda. static bool partial_application(pass_opt_t* opt, ast_t** astp) { /* Example that we refer to throughout this function. * ```pony * class C * fun f[T](a: A, b: B = b_default): R * * let recv: T = ... * recv~f[T2](foo) * ``` * * Partial call is converted to: * ```pony * lambda(b: B = b_default)($0 = recv, a = foo): R => $0.f[T2](a, consume b) * ``` */ ast_t* ast = *astp; typecheck_t* t = &opt->check; if(!method_application(opt, ast, true)) return false; AST_GET_CHILDREN(ast, positional, namedargs, lhs); assert(ast_id(lhs) == TK_FUNAPP || ast_id(lhs) == TK_BEAPP || ast_id(lhs) == TK_NEWAPP); // LHS must be a TK_TILDE, possibly contained in a TK_QUALIFY. AST_GET_CHILDREN(lhs, receiver, method); ast_t* type_args = NULL; switch(ast_id(receiver)) { case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: type_args = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // The TK_FUNTYPE of the LHS. ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; token_id apply_cap = partial_application_cap(opt, type, receiver, positional); AST_GET_CHILDREN(type, cap, type_params, target_params, result); token_id can_error = ast_canerror(lhs) ? TK_QUESTION : TK_NONE; const char* recv_name = package_hygienic_id(t); // Build captures. We always have at least one capture, for receiver. // Capture: `$0 = recv` BUILD(captures, receiver, NODE(TK_LAMBDACAPTURES, NODE(TK_LAMBDACAPTURE, ID(recv_name) NONE // Infer type. TREE(receiver)))); // Process arguments. ast_t* given_arg = ast_child(positional); ast_t* target_param = ast_child(target_params); ast_t* lambda_params = ast_from(target_params, TK_NONE); ast_t* lambda_call_args = ast_from(positional, TK_NONE); while(given_arg != NULL) { assert(target_param != NULL); const char* target_p_name = ast_name(ast_child(target_param)); if(ast_id(given_arg) == TK_NONE) { // This argument is not supplied already, must be a lambda parameter. // Like `b` in example above. // Build a new a new TK_PARAM node rather than copying the target one, // since the target has already been processed to expr pass, and we need // a clean one. AST_GET_CHILDREN(target_param, p_id, p_type, p_default); // Parameter: `b: B = b_default` BUILD(lambda_param, target_param, NODE(TK_PARAM, TREE(p_id) TREE(sanitise_type(p_type)) TREE(p_default))); ast_append(lambda_params, lambda_param); ast_setid(lambda_params, TK_PARAMS); // Argument: `consume b` BUILD(target_arg, lambda_param, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(target_p_name))))); ast_append(lambda_call_args, target_arg); ast_setid(lambda_call_args, TK_POSITIONALARGS); } else { // This argument is supplied to the partial, capture it. // Like `a` in example above. // Capture: `a = foo` BUILD(capture, given_arg, NODE(TK_LAMBDACAPTURE, ID(target_p_name) NONE TREE(given_arg))); ast_append(captures, capture); // Argument: `a` BUILD(target_arg, given_arg, NODE(TK_SEQ, NODE(TK_REFERENCE, ID(target_p_name)))); ast_append(lambda_call_args, target_arg); ast_setid(lambda_call_args, TK_POSITIONALARGS); } given_arg = ast_sibling(given_arg); target_param = ast_sibling(target_param); } assert(target_param == NULL); // Build lambda expression. // `$0.f` BUILD(call_receiver, ast, NODE(TK_DOT, NODE(TK_REFERENCE, ID(recv_name)) TREE(method))); if(type_args != NULL) { // The partial call has type args, add them to the actual call in apply(). // `$0.f[T2]` BUILD(qualified, type_args, NODE(TK_QUALIFY, TREE(call_receiver) TREE(type_args))); call_receiver = qualified; } REPLACE(astp, NODE(TK_LAMBDA, NODE(apply_cap) NONE // Lambda function name. NONE // Lambda type params. TREE(lambda_params) TREE(captures) TREE(sanitise_type(result)) NODE(can_error) NODE(TK_SEQ, NODE(TK_CALL, TREE(lambda_call_args) NONE // Named args. TREE(call_receiver))))); // Need to preserve various lambda children. ast_setflag(ast_childidx(*astp, 2), AST_FLAG_PRESERVE); // Type params. ast_setflag(ast_childidx(*astp, 3), AST_FLAG_PRESERVE); // Parameters. ast_setflag(ast_childidx(*astp, 5), AST_FLAG_PRESERVE); // Return type. ast_setflag(ast_childidx(*astp, 7), AST_FLAG_PRESERVE); // Body. // Catch up to this pass. return ast_passes_subtree(astp, opt, PASS_EXPR); }
// Check resulting methods are compatible with the containing entity and patch // up symbol tables static bool post_process_methods(ast_t* entity, pass_opt_t* options, bool is_concrete) { assert(entity != NULL); bool r = true; ast_t* members = ast_childidx(entity, 4); for(ast_t* m = ast_child(members); m != NULL; m = ast_sibling(m)) { token_id variety = ast_id(m); // Check behaviour compatability if(variety == TK_BE) { switch(ast_id(entity)) { case TK_PRIMITIVE: ast_error(entity, "primitives can't provide traits that have behaviours"); r = false; break; case TK_CLASS: ast_error(entity, "classes can't have provide that have behaviours"); r = false; break; default: break; } } if(variety == TK_BE || variety == TK_FUN || variety == TK_NEW) { // Check concrete method bodies if(ast_data(m) == BODY_AMBIGUOUS) { if(is_concrete) { ast_error(m, "multiple possible method bodies from traits"); r = false; } } else if(ast_data(m) == NULL) { if(is_concrete) { assert(ast_id(ast_childidx(m, 6)) == TK_NONE); ast_error(m, "no body found for method %d %s %s", is_concrete, ast_get_print(entity), ast_name(ast_child(entity))); r = false; } } else { assert(ast_id(ast_childidx(m, 6)) != TK_NONE); if(ast_data(m) != entity) { // Sort out copied symbol tables ast_visit(&m, rescope, NULL, options); } } } } return r; }
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; }
// Add all methods from the provides list of the given entity into lists in the // given symbol table. static bool collate_provided(ast_t* entity, methods_t* method_info) { assert(entity != NULL); bool r = true; ast_t* traits = ast_childidx(entity, 3); for(ast_t* t = ast_child(traits); t != NULL; t = ast_sibling(t)) { assert(ast_id(t) == TK_NOMINAL); ast_t* trait_def = (ast_t*)ast_data(t); assert(trait_def != NULL); // TODO: Check whether we need an error here if((ast_id(trait_def) != TK_TRAIT) && (ast_id(trait_def) != TK_INTERFACE)) return false; // Check for duplicates in our provides list // This is just simple compare of each entry against all the other. This is // clearly O(n^2), but since provides lists are likely to be small that // should be OK. If it turns out to be a problem it can be changed later. for(ast_t* p = ast_child(traits); p != t; p = ast_sibling(p)) { if(trait_def == (ast_t*)ast_data(p)) { ast_error(t, "duplicate entry in provides list"); ast_error(p, "previous entry here"); } } if(!build_entity_def(trait_def)) return false; ast_t* type_params = ast_childidx(trait_def, 1); ast_t* type_args = ast_childidx(t, 2); ast_t* trait_methods = ast_childidx(trait_def, 4); for(ast_t* m = ast_child(trait_methods); m != NULL; m = ast_sibling(m)) { // Reify the method with the type parameters from trait definition and // the reified type arguments from trait reference ast_t* r_method = reify(m, type_params, type_args); const char* entity_name = ast_name(ast_child(entity)); if(ast_id(r_method) == TK_BE || ast_id(r_method) == TK_NEW) { // Modify return type to the inheritting type ast_t* ret_type = ast_childidx(r_method, 4); assert(ast_id(ret_type) == TK_NOMINAL); const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE)); ast_set_name(ast_childidx(ret_type, 0), pkg_name); ast_set_name(ast_childidx(ret_type, 1), entity_name); } if(!add_method_to_list(r_method, method_info, entity_name)) r = false; } } return r; }
void genprim_array_deserialise(compile_t* c, reach_type_t* t) { // Generate the deserisalise function. t->deserialise_fn = codegen_addfun(c, genname_serialise(t->name), c->trace_type); codegen_startfun(c, t->deserialise_fn, NULL, NULL); LLVMSetFunctionCallConv(t->deserialise_fn, LLVMCCallConv); LLVMValueRef ctx = LLVMGetParam(t->deserialise_fn, 0); LLVMValueRef arg = LLVMGetParam(t->deserialise_fn, 1); LLVMValueRef object = LLVMBuildBitCast(c->builder, arg, t->structure_ptr, ""); gendeserialise_typeid(c, t, object); // Deserialise the array contents. LLVMValueRef alloc = field_value(c, object, 2); LLVMValueRef ptr_offset = field_value(c, object, 3); ptr_offset = LLVMBuildPtrToInt(c->builder, ptr_offset, c->intptr, ""); ast_t* typeargs = ast_childidx(t->ast, 2); ast_t* typearg = ast_child(typeargs); reach_type_t* t_elem = reach_type(c->reach, typearg); size_t abisize = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type); LLVMValueRef l_size = LLVMConstInt(c->intptr, abisize, false); LLVMValueRef args[3]; args[0] = ctx; args[1] = ptr_offset; args[2] = LLVMBuildMul(c->builder, alloc, l_size, ""); LLVMValueRef ptr = gencall_runtime(c, "pony_deserialise_block", args, 3, ""); LLVMValueRef ptr_loc = LLVMBuildStructGEP(c->builder, object, 3, ""); LLVMBuildStore(c->builder, ptr, ptr_loc); if((t_elem->underlying == TK_PRIMITIVE) && (t_elem->primitive != NULL)) { // Do nothing. A memcpy is sufficient. } else { LLVMValueRef size = field_value(c, object, 1); ptr = LLVMBuildBitCast(c->builder, ptr, LLVMPointerType(t_elem->use_type, 0), ""); LLVMBasicBlockRef entry_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef cond_block = codegen_block(c, "cond"); LLVMBasicBlockRef body_block = codegen_block(c, "body"); LLVMBasicBlockRef post_block = codegen_block(c, "post"); LLVMBuildBr(c->builder, cond_block); // While the index is less than the size, deserialise an element. The // initial index when coming from the entry block is zero. LLVMPositionBuilderAtEnd(c->builder, cond_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->intptr, ""); LLVMValueRef zero = LLVMConstInt(c->intptr, 0, false); LLVMAddIncoming(phi, &zero, &entry_block, 1); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntULT, phi, size, ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); // The phi node is the index. Get the element and deserialise it. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef elem_ptr = LLVMBuildGEP(c->builder, ptr, &phi, 1, ""); gendeserialise_element(c, t_elem, false, ctx, elem_ptr); // Add one to the phi node and branch back to the cond block. LLVMValueRef one = LLVMConstInt(c->intptr, 1, false); LLVMValueRef inc = LLVMBuildAdd(c->builder, phi, one, ""); body_block = LLVMGetInsertBlock(c->builder); LLVMAddIncoming(phi, &inc, &body_block, 1); LLVMBuildBr(c->builder, cond_block); LLVMPositionBuilderAtEnd(c->builder, post_block); } LLVMBuildRetVoid(c->builder); codegen_finishfun(c); }
void genprim_array_serialise(compile_t* c, reach_type_t* t) { // Generate the serialise function. t->serialise_fn = codegen_addfun(c, genname_serialise(t->name), c->serialise_type); codegen_startfun(c, t->serialise_fn, NULL, NULL); LLVMSetFunctionCallConv(t->serialise_fn, LLVMCCallConv); LLVMValueRef ctx = LLVMGetParam(t->serialise_fn, 0); LLVMValueRef arg = LLVMGetParam(t->serialise_fn, 1); LLVMValueRef addr = LLVMGetParam(t->serialise_fn, 2); LLVMValueRef offset = LLVMGetParam(t->serialise_fn, 3); LLVMValueRef mut = LLVMGetParam(t->serialise_fn, 4); LLVMValueRef object = LLVMBuildBitCast(c->builder, arg, t->structure_ptr, ""); LLVMValueRef offset_addr = LLVMBuildAdd(c->builder, LLVMBuildPtrToInt(c->builder, addr, c->intptr, ""), offset, ""); genserialise_typeid(c, t, offset_addr); // Don't serialise our contents if we are opaque. LLVMBasicBlockRef body_block = codegen_block(c, "body"); LLVMBasicBlockRef post_block = codegen_block(c, "post"); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntNE, mut, LLVMConstInt(c->i32, PONY_TRACE_OPAQUE, false), ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); LLVMPositionBuilderAtEnd(c->builder, body_block); // Write the size twice, effectively rewriting alloc to be the same as size. LLVMValueRef size = field_value(c, object, 1); LLVMValueRef size_loc = field_loc(c, offset_addr, t->structure, c->intptr, 1); LLVMBuildStore(c->builder, size, size_loc); LLVMValueRef alloc_loc = field_loc(c, offset_addr, t->structure, c->intptr, 2); LLVMBuildStore(c->builder, size, alloc_loc); // Write the pointer. LLVMValueRef ptr = field_value(c, object, 3); // The resulting offset will only be invalid (i.e. have the high bit set) if // the size is zero. For an opaque array, we don't serialise the contents, // so we don't get here, so we don't end up with an invalid offset. LLVMValueRef args[5]; args[0] = ctx; args[1] = ptr; LLVMValueRef ptr_offset = gencall_runtime(c, "pony_serialise_offset", args, 2, ""); LLVMValueRef ptr_loc = field_loc(c, offset_addr, t->structure, c->intptr, 3); LLVMBuildStore(c->builder, ptr_offset, ptr_loc); LLVMValueRef ptr_offset_addr = LLVMBuildAdd(c->builder, ptr_offset, LLVMBuildPtrToInt(c->builder, addr, c->intptr, ""), ""); // Serialise elements. ast_t* typeargs = ast_childidx(t->ast, 2); ast_t* typearg = ast_child(typeargs); reach_type_t* t_elem = reach_type(c->reach, typearg); size_t abisize = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type); LLVMValueRef l_size = LLVMConstInt(c->intptr, abisize, false); if((t_elem->underlying == TK_PRIMITIVE) && (t_elem->primitive != NULL)) { // memcpy machine words args[0] = LLVMBuildIntToPtr(c->builder, ptr_offset_addr, c->void_ptr, ""); args[1] = LLVMBuildBitCast(c->builder, ptr, c->void_ptr, ""); args[2] = LLVMBuildMul(c->builder, size, l_size, ""); args[3] = LLVMConstInt(c->i32, 1, false); args[4] = LLVMConstInt(c->i1, 0, false); if(target_is_ilp32(c->opt->triple)) { gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i32", args, 5, ""); } else { gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i64", args, 5, ""); } } else { ptr = LLVMBuildBitCast(c->builder, ptr, LLVMPointerType(t_elem->use_type, 0), ""); LLVMBasicBlockRef entry_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef cond_block = codegen_block(c, "cond"); LLVMBasicBlockRef body_block = codegen_block(c, "body"); LLVMBasicBlockRef post_block = codegen_block(c, "post"); LLVMValueRef offset_var = LLVMBuildAlloca(c->builder, c->intptr, ""); LLVMBuildStore(c->builder, ptr_offset_addr, offset_var); LLVMBuildBr(c->builder, cond_block); // While the index is less than the size, serialise an element. The // initial index when coming from the entry block is zero. LLVMPositionBuilderAtEnd(c->builder, cond_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->intptr, ""); LLVMValueRef zero = LLVMConstInt(c->intptr, 0, false); LLVMAddIncoming(phi, &zero, &entry_block, 1); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntULT, phi, size, ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); // The phi node is the index. Get the element and serialise it. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef elem_ptr = LLVMBuildGEP(c->builder, ptr, &phi, 1, ""); ptr_offset_addr = LLVMBuildLoad(c->builder, offset_var, ""); genserialise_element(c, t_elem, false, ctx, elem_ptr, ptr_offset_addr); ptr_offset_addr = LLVMBuildAdd(c->builder, ptr_offset_addr, l_size, ""); LLVMBuildStore(c->builder, ptr_offset_addr, offset_var); // Add one to the phi node and branch back to the cond block. LLVMValueRef one = LLVMConstInt(c->intptr, 1, false); LLVMValueRef inc = LLVMBuildAdd(c->builder, phi, one, ""); body_block = LLVMGetInsertBlock(c->builder); LLVMAddIncoming(phi, &inc, &body_block, 1); LLVMBuildBr(c->builder, cond_block); LLVMPositionBuilderAtEnd(c->builder, post_block); } LLVMBuildBr(c->builder, post_block); LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMBuildRetVoid(c->builder); codegen_finishfun(c); }
bool expr_addressof(pass_opt_t* opt, ast_t* ast) { // Check if we're in an FFI call. ast_t* parent = ast_parent(ast); bool ok = false; if(ast_id(parent) == TK_SEQ) { parent = ast_parent(parent); if(ast_id(parent) == TK_POSITIONALARGS) { parent = ast_parent(parent); if(ast_id(parent) == TK_FFICALL) ok = true; } } if(!ok) { ast_error(opt->check.errors, ast, "the addressof operator can only be used for FFI arguments"); return false; } ast_t* expr = ast_child(ast); switch(ast_id(expr)) { case TK_FVARREF: case TK_VARREF: case TK_FUNREF: case TK_BEREF: break; case TK_FLETREF: ast_error(opt->check.errors, ast, "can't take the address of a let field"); return false; case TK_EMBEDREF: ast_error(opt->check.errors, ast, "can't take the address of an embed field"); return false; case TK_LETREF: ast_error(opt->check.errors, ast, "can't take the address of a let local"); return false; case TK_PARAMREF: ast_error(opt->check.errors, ast, "can't take the address of a function parameter"); return false; default: ast_error(opt->check.errors, ast, "can only take the address of a local, field or method"); return false; } // Set the type to Pointer[ast_type(expr)]. Set to Pointer[None] for function // pointers. ast_t* expr_type = ast_type(expr); if(is_typecheck_error(expr_type)) return false; switch(ast_id(expr)) { case TK_FUNREF: case TK_BEREF: expr_type = type_builtin(opt, ast, "None"); break; default: {} } ast_t* type = type_pointer_to(opt, expr_type); ast_settype(ast, type); return true; }
// Write a description of the given entity to its own type file. // The containing package is handed in to save looking it up again. static void doc_entity(docgen_t* docgen, ast_t* ast, ast_t* package) { assert(docgen != NULL); assert(docgen->index_file != NULL); assert(docgen->type_file == NULL); assert(ast != NULL); assert(package != NULL); // First open a file size_t tqfn_len; char* tqfn = write_tqfn(ast, NULL, &tqfn_len); docgen->type_file = doc_open_file(docgen, true, tqfn, ".md"); if(docgen->type_file == NULL) return; // Add reference to new file to index file AST_GET_CHILDREN(ast, id, tparams, cap, provides, members, c_api, doc); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->index_file, " - %s %s: \"%s.md\"\n", ast_get_print(ast), name, tqfn); pool_free_size(tqfn_len, tqfn); // Now we can write the actual documentation for the entity fprintf(docgen->type_file, "%s %s", ast_get_print(ast), name); doc_type_params(docgen, tparams); doc_type_list(docgen, provides, " is ", ", ", ""); fprintf(docgen->type_file, "\n\nIn package \"%s\".\n\n", package_qualified_name(package)); fprintf(docgen->type_file, "%s", (name[0] == '_') ? "Private" : "Public"); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, ", default capability %s", cap_text); fprintf(docgen->type_file, ".\n\n"); if(ast_id(c_api) == TK_AT) fprintf(docgen->type_file, "May be called from C.\n"); if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); else fprintf(docgen->type_file, "No doc string provided.\n\n"); // Sort members into varieties ast_list_t pub_fields = { NULL, NULL, NULL }; ast_list_t news = { NULL, NULL, NULL }; ast_list_t bes = { NULL, NULL, NULL }; ast_list_t funs = { NULL, NULL, NULL }; for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { switch(ast_id(p)) { case TK_FVAR: case TK_FLET: doc_list_add_named(&pub_fields, p, 0, true, false); break; case TK_NEW: doc_list_add_named(&news, p, 1, true, true); break; case TK_BE: doc_list_add_named(&bes, p, 1, true, true); break; case TK_FUN: doc_list_add_named(&funs, p, 1, true, true); break; default: assert(0); break; } } // Handle member variety lists doc_fields(docgen, &pub_fields, "Public fields"); doc_methods(docgen, &news, "Constructors"); doc_methods(docgen, &bes, "Behaviours"); doc_methods(docgen, &funs, "Functions"); doc_list_free(&pub_fields); doc_list_free(&news); doc_list_free(&bes); doc_list_free(&funs); fclose(docgen->type_file); docgen->type_file = NULL; }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->def_arg != NULL) { ast_error(opt->check.errors, ast, "can't reference 'this' in a default argument"); return false; } sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(opt->check.errors, ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) { ast_t* parent = ast_parent(ast); if(ast_id(parent) != TK_DOT) cap = TK_TAG; } bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); type = arrow; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(opt->check.errors, ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(opt->check.errors, ast, "couldn't create a type for 'this'"); ast_free(type); return false; } // Unless this is a field lookup, treat an incomplete `this` as a tag. ast_t* parent = ast_parent(ast); bool incomplete_ok = false; if((ast_id(parent) == TK_DOT) && (ast_child(parent) == ast)) { ast_t* right = ast_sibling(ast); assert(ast_id(right) == TK_ID); ast_t* find = lookup_try(opt, ast, nominal, ast_name(right)); if(find != NULL) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: incomplete_ok = true; break; default: {} } } } if(!incomplete_ok && is_this_incomplete(t, ast)) { ast_t* tag_type = set_cap_and_ephemeral(nominal, TK_TAG, TK_NONE); ast_replace(&nominal, tag_type); } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }
static bool init_module(compile_t* c, ast_t* program, pass_opt_t* opt) { c->opt = opt; // Get the first package and the builtin package. ast_t* package = ast_child(program); ast_t* builtin = ast_sibling(package); // If we have only one package, we are compiling builtin itself. if(builtin == NULL) builtin = package; // The name of the first package is the name of the program. c->filename = package_filename(package); // LLVM context and machine settings. if(c->opt->library || target_is_ilp32(opt->triple)) c->callconv = LLVMCCallConv; else c->callconv = LLVMFastCallConv; if(!c->opt->release || c->opt->library || c->opt->extfun) c->linkage = LLVMExternalLinkage; else c->linkage = LLVMPrivateLinkage; c->machine = make_machine(opt); if(c->machine == NULL) return false; c->context = LLVMContextCreate(); // Create a module. c->module = LLVMModuleCreateWithNameInContext(c->filename, c->context); // Set the target triple. LLVMSetTarget(c->module, opt->triple); // Set the data layout. #if PONY_LLVM <= 308 c->target_data = LLVMGetTargetMachineData(c->machine); #else c->target_data = LLVMCreateTargetDataLayout(c->machine); #endif char* layout = LLVMCopyStringRepOfTargetData(c->target_data); LLVMSetDataLayout(c->module, layout); LLVMDisposeMessage(layout); // IR builder. c->builder = LLVMCreateBuilderInContext(c->context); c->di = LLVMNewDIBuilder(c->module); // TODO: what LANG id should be used? c->di_unit = LLVMDIBuilderCreateCompileUnit(c->di, 0x0004, package_filename(package), package_path(package), "ponyc-" PONY_VERSION, c->opt->release); // Empty frame stack. c->frame = NULL; c->reach = reach_new(); c->tbaa_mds = tbaa_metadatas_new(); // This gets a real value once the instance of None has been generated. c->none_instance = NULL; return true; }
LLVMValueRef gen_digestof(compile_t* c, ast_t* ast) { ast_t* expr = ast_child(ast); LLVMValueRef value = gen_expr(c, expr); return gen_digestof_value(c, value); }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) cap = TK_TAG; ast_t* type = type_for_this(t, ast, cap, TK_NONE); if(t->frame->def_arg != NULL) { ast_error(ast, "can't reference 'this' in a default argument"); return false; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }
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 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 void setup_type_fields(gentype_t* g) { assert(ast_id(g->ast) == TK_NOMINAL); g->field_count = 0; g->fields = NULL; g->field_keys = NULL; ast_t* def = (ast_t*)ast_data(g->ast); if(ast_id(def) == TK_PRIMITIVE) return; ast_t* typeargs = ast_childidx(g->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: { g->field_count++; break; } default: {} } member = ast_sibling(member); } if(g->field_count == 0) return; g->fields = (ast_t**)calloc(g->field_count, sizeof(ast_t*)); g->field_keys = (token_id*)calloc(g->field_count, sizeof(token_id)); 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_GET_CHILDREN(member, name, type, init); g->fields[index] = reify(ast_type(member), typeparams, typeargs); // TODO: Are we sure the AST source file is correct? ast_setpos(g->fields[index], NULL, ast_line(name), ast_pos(name)); g->field_keys[index] = ast_id(member); index++; break; } default: {} } member = ast_sibling(member); } }
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); }
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; }
bool expr_nominal(pass_opt_t* opt, ast_t** astp) { // Resolve type aliases and typeparam references. if(!names_nominal(opt, *astp, astp, true)) return false; ast_t* ast = *astp; switch(ast_id(ast)) { case TK_TYPEPARAMREF: return flatten_typeparamref(opt, ast) == AST_OK; case TK_NOMINAL: break; default: return true; } // If still nominal, check constraints. ast_t* def = (ast_t*)ast_data(ast); // Special case: don't check the constraint of a Pointer or an Array. These // builtin types have no contraint on their type parameter, and it is safe // to bind a struct as a type argument (which is not safe on any user defined // type, as that type might then be used for pattern matching). if(is_pointer(ast) || is_literal(ast, "Array")) return true; ast_t* typeparams = ast_childidx(def, 1); ast_t* typeargs = ast_childidx(ast, 2); if(!reify_defaults(typeparams, typeargs, true, opt)) return false; if(is_maybe(ast)) { // MaybePointer[A] must be bound to a struct. assert(ast_childcount(typeargs) == 1); ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); bool ok = false; switch(ast_id(typearg)) { case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(typearg); ok = ast_id(def) == TK_STRUCT; break; } case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(typearg); ok = def == typeparam; break; } default: {} } if(!ok) { ast_error(opt->check.errors, ast, "%s is not allowed: " "the type argument to MaybePointer must be a struct", ast_print_type(ast)); return false; } return true; } return check_constraints(typeargs, typeparams, typeargs, true, opt); }
bool is_constructable(ast_t* type) { switch(ast_id(type)) { case TK_UNIONTYPE: case TK_TUPLETYPE: return false; case TK_ISECTTYPE: { ast_t* child = ast_child(type); while(child != NULL) { if(is_constructable(child)) return true; child = ast_sibling(child); } return false; } case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(type); switch(ast_id(def)) { case TK_INTERFACE: case TK_TRAIT: { ast_t* members = ast_childidx(def, 4); ast_t* member = ast_child(members); while(member != NULL) { if(ast_id(member) == TK_NEW) return true; member = ast_sibling(member); } return false; } case TK_PRIMITIVE: case TK_CLASS: case TK_ACTOR: return true; default: {} } break; } case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(type); ast_t* constraint = ast_childidx(def, 1); ast_t* constraint_def = (ast_t*)ast_data(constraint); if(def == constraint_def) return false; return is_constructable(constraint); } case TK_ARROW: return is_constructable(ast_childidx(type, 1)); default: {} } assert(0); return false; }
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; }
bool expr_reference(pass_opt_t* opt, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; // Everything we reference must be in scope. const char* name = ast_name(ast_child(ast)); sym_status_t status; ast_t* def = ast_get(ast, name, &status); if(def == NULL) { const char* alt_name = suggest_alt_name(ast, name); if(alt_name == NULL) ast_error(ast, "can't find declaration of '%s'", name); else ast_error(ast, "can't find declaration of '%s', did you mean '%s'?", name, alt_name); return false; } switch(ast_id(def)) { case TK_PACKAGE: { // Only allowed if in a TK_DOT with a type. if(ast_id(ast_parent(ast)) != TK_DOT) { ast_error(ast, "a package can only appear as a prefix to a type"); return false; } ast_setid(ast, TK_PACKAGEREF); return true; } case TK_INTERFACE: case TK_TRAIT: case TK_TYPE: case TK_TYPEPARAM: case TK_PRIMITIVE: case TK_STRUCT: case TK_CLASS: case TK_ACTOR: { // It's a type name. This may not be a valid type, since it may need // type arguments. ast_t* id = ast_child(def); const char* name = ast_name(id); ast_t* type = type_sugar(ast, NULL, name); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_FVAR: case TK_FLET: case TK_EMBED: { // Transform to "this.f". if(!def_before_use(def, ast, name)) return false; ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_child(ast)); ast_t* self = ast_from(ast, TK_THIS); ast_add(dot, self); ast_replace(astp, dot); if(!expr_this(opt, self)) return false; return expr_dot(opt, astp); } case TK_PARAM: { if(t->frame->def_arg != NULL) { ast_error(ast, "can't reference a parameter in a default argument"); return false; } if(!def_before_use(def, ast, name)) return false; ast_t* type = ast_type(def); if(is_typecheck_error(type)) return false; if(!valid_reference(opt, ast, type, status)) return false; if(!sendable(type) && (t->frame->recover != NULL)) { ast_error(ast, "can't access a non-sendable parameter from inside a recover " "expression"); return false; } // Get the type of the parameter and attach it to our reference. // Automatically consume a parameter if the function is done. ast_t* r_type = type; if(is_method_return(t, ast)) r_type = consume_type(type, TK_NONE); ast_settype(ast, r_type); ast_setid(ast, TK_PARAMREF); return true; } case TK_NEW: case TK_BE: case TK_FUN: { // Transform to "this.f". ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_child(ast)); ast_t* self = ast_from(ast, TK_THIS); ast_add(dot, self); ast_replace(astp, dot); if(!expr_this(opt, self)) return false; return expr_dot(opt, astp); } case TK_ID: { if(!def_before_use(def, ast, name)) return false; ast_t* type = ast_type(def); if(type != NULL && ast_id(type) == TK_INFERTYPE) { ast_error(ast, "cannot infer type of %s\n", ast_nice_name(def)); ast_settype(def, ast_from(def, TK_ERRORTYPE)); ast_settype(ast, ast_from(ast, TK_ERRORTYPE)); return false; } if(is_typecheck_error(type)) return false; if(!valid_reference(opt, ast, type, status)) return false; ast_t* var = ast_parent(def); switch(ast_id(var)) { case TK_VAR: ast_setid(ast, TK_VARREF); break; case TK_LET: case TK_MATCH_CAPTURE: ast_setid(ast, TK_LETREF); break; default: assert(0); return false; } if(!sendable(type)) { if(t->frame->recover != NULL) { ast_t* def_recover = ast_nearest(def, TK_RECOVER); if(t->frame->recover != def_recover) { ast_error(ast, "can't access a non-sendable local defined outside " "of a recover expression from within that recover expression"); return false; } } } // Get the type of the local and attach it to our reference. // Automatically consume a local if the function is done. ast_t* r_type = type; if(is_method_return(t, ast)) r_type = consume_type(type, TK_NONE); ast_settype(ast, r_type); return true; } default: {} } assert(0); return false; }
static reach_type_t* add_nominal(reach_t* r, ast_t* type, pass_opt_t* opt) { reach_type_t* t = reach_type(r, type); if(t != NULL) return t; t = add_reach_type(r, type); ast_t* def = (ast_t*)ast_data(type); t->underlying = ast_id(def); AST_GET_CHILDREN(type, pkg, id, typeparams); ast_t* typeparam = ast_child(typeparams); while(typeparam != NULL) { add_type(r, typeparam, opt); typeparam = ast_sibling(typeparam); } switch(ast_id(def)) { case TK_INTERFACE: case TK_TRAIT: add_types_to_trait(r, t, opt); break; case TK_PRIMITIVE: add_traits_to_type(r, t, opt); add_special(r, t, type, "_init", opt); add_special(r, t, type, "_final", opt); break; case TK_STRUCT: case TK_CLASS: add_traits_to_type(r, t, opt); add_special(r, t, type, "_final", opt); add_fields(r, t, opt); break; case TK_ACTOR: add_traits_to_type(r, t, opt); add_special(r, t, type, "_event_notify", opt); add_special(r, t, type, "_final", opt); add_fields(r, t, opt); break; default: {} } if(t->type_id == (uint32_t)-1) t->type_id = r->next_type_id++; if(ast_id(def) != TK_PRIMITIVE) return t; if(strcmp(ast_name(pkg), "$0")) return t; const char* name = ast_name(id); if(name[0] == 'I') { if(!strcmp(name, "I8")) t->mangle = "c"; else if(!strcmp(name, "I16")) t->mangle = "s"; else if(!strcmp(name, "I32")) t->mangle = "i"; else if(!strcmp(name, "I64")) t->mangle = "w"; else if(!strcmp(name, "I128")) t->mangle = "q"; else if(!strcmp(name, "ILong")) t->mangle = "l"; else if(!strcmp(name, "ISize")) t->mangle = "z"; } else if(name[0] == 'U') { if(!strcmp(name, "U8")) t->mangle = "C"; else if(!strcmp(name, "U16")) t->mangle = "S"; else if(!strcmp(name, "U32")) t->mangle = "I"; else if(!strcmp(name, "U64")) t->mangle = "W"; else if(!strcmp(name, "U128")) t->mangle = "Q"; else if(!strcmp(name, "ULong")) t->mangle = "L"; else if(!strcmp(name, "USize")) t->mangle = "Z"; } else if(name[0] == 'F') { if(!strcmp(name, "F32")) t->mangle = "f"; else if(!strcmp(name, "F64")) t->mangle = "d"; } else if(!strcmp(name, "Bool")) { t->mangle = "b"; } return t; }