LLVMValueRef gen_shr(compile_t* c, ast_t* left, ast_t* right) { ast_t* type = ast_type(left); bool sign = is_signed(type); LLVMValueRef l_value = gen_expr(c, left); LLVMValueRef r_value = gen_expr(c, right); if((l_value == NULL) || (r_value == NULL)) return NULL; if(LLVMIsConstant(l_value) && LLVMIsConstant(r_value)) { if(sign) return LLVMConstAShr(l_value, r_value); return LLVMConstLShr(l_value, r_value); } if(sign) return LLVMBuildAShr(c->builder, l_value, r_value, ""); return LLVMBuildLShr(c->builder, l_value, r_value, ""); }
static bool case_body(compile_t* c, ast_t* body, LLVMBasicBlockRef post_block, LLVMValueRef phi, LLVMTypeRef phi_type) { LLVMValueRef body_value = gen_expr(c, body); // If it returns, we don't branch to the post block. if(body_value == GEN_NOVALUE) return true; if(is_result_needed(body)) { ast_t* body_type = ast_type(body); body_value = gen_assign_cast(c, phi_type, body_value, body_type); if(body_value == NULL) return false; LLVMBasicBlockRef block = LLVMGetInsertBlock(c->builder); LLVMAddIncoming(phi, &body_value, &block, 1); } LLVMBuildBr(c->builder, post_block); return true; }
bool expr_recover(ast_t* ast) { AST_GET_CHILDREN(ast, cap, expr); ast_t* type = ast_type(expr); if(is_typecheck_error(type)) return false; ast_t* r_type = recover_type(type, ast_id(cap)); if(r_type == NULL) { ast_error(ast, "can't recover to this capability"); ast_error(expr, "expression type is %s", ast_print_type(type)); return false; } ast_settype(ast, r_type); ast_inheritflags(ast); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
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; }
// Coerce a literal expression to given tuple or non-tuple types static bool coerce_literal_to_type(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); if(lit_type == NULL || (ast_id(lit_type) != TK_LITERAL && ast_id(lit_type) != TK_OPERATORLITERAL)) { // Not a literal return true; } if(ast_child(lit_type) != NULL) { // Control block literal return coerce_control_block(astp, target_type, chain, options, report_errors); } switch(ast_id(literal_expr)) { case TK_TUPLE: // Tuple literal { size_t cardinality = ast_childcount(literal_expr); if(!coerce_group(astp, target_type, chain, cardinality, options, report_errors)) return false; break; } case TK_INT: return uif_type_from_chain(options, literal_expr, target_type, chain, false, report_errors); case TK_FLOAT: return uif_type_from_chain(options, literal_expr, target_type, chain, true, report_errors); case TK_ARRAY: if(!coerce_group(astp, target_type, chain, CHAIN_CARD_ARRAY, options, report_errors)) return false; break; case TK_SEQ: { // Only coerce the last expression in the sequence ast_t* last = ast_childlast(literal_expr); if(!coerce_literal_to_type(&last, target_type, chain, options, report_errors)) return false; ast_settype(literal_expr, ast_type(last)); return true; } case TK_CALL: { AST_GET_CHILDREN(literal_expr, positional, named, receiver); ast_t* arg = ast_child(positional); if(!coerce_literal_to_type(&receiver, target_type, chain, options, report_errors)) return false; if(arg != NULL && !coerce_literal_to_type(&arg, target_type, chain, options, report_errors)) return false; ast_settype(literal_expr, ast_type(ast_child(receiver))); return true; } case TK_DOT: { ast_t* receiver = ast_child(literal_expr); if(!coerce_literal_to_type(&receiver, target_type, chain, options, report_errors)) return false; break; } default: ast_error(literal_expr, "Internal error, coerce_literal_to_type node %s", ast_get_print(literal_expr)); assert(0); return false; } // Need to reprocess node now all the literals have types ast_settype(literal_expr, NULL); return (pass_expr(astp, options) == AST_OK); }
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(ast, "the & 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(ast, "can't take the address of a let field"); return false; case TK_EMBEDREF: ast_error(ast, "can't take the address of an embed field"); return false; case TK_LETREF: ast_error(ast, "can't take the address of a let local"); return false; case TK_PARAMREF: ast_error(ast, "can't take the address of a function parameter"); return false; default: ast_error(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; }
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 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 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); } }
LLVMValueRef gen_pattern_eq(compile_t* c, ast_t* pattern, LLVMValueRef r_value) { // This is used for structural equality in pattern matching. ast_t* pattern_type = ast_type(pattern); if(ast_id(pattern_type) == TK_NOMINAL) { AST_GET_CHILDREN(pattern_type, package, id); // Special case equality on primitive types. if(ast_name(package) == c->str_builtin) { const char* name = ast_name(id); if((name == c->str_Bool) || (name == c->str_I8) || (name == c->str_I16) || (name == c->str_I32) || (name == c->str_I64) || (name == c->str_I128) || (name == c->str_ILong) || (name == c->str_ISize) || (name == c->str_U8) || (name == c->str_U16) || (name == c->str_U32) || (name == c->str_U64) || (name == c->str_U128) || (name == c->str_ULong) || (name == c->str_USize) || (name == c->str_F32) || (name == c->str_F64) ) { return gen_eq_rvalue(c, pattern, r_value, true); } } } // Generate the receiver. LLVMValueRef l_value = gen_expr(c, pattern); reach_type_t* t = reach_type(c->reach, pattern_type); pony_assert(t != NULL); // Static or virtual dispatch. token_id cap = cap_dispatch(pattern_type); reach_method_t* m = reach_method(t, cap, c->str_eq, NULL); LLVMValueRef func = dispatch_function(c, t, m, l_value); if(func == NULL) return NULL; // Call the function. We know it isn't partial. LLVMValueRef args[2]; args[0] = l_value; args[1] = r_value; codegen_debugloc(c, pattern); LLVMValueRef result = codegen_call(c, func, args, 2); codegen_debugloc(c, NULL); return result; }
LLVMValueRef gen_call(compile_t* c, ast_t* ast) { // Special case calls. LLVMValueRef special; if(special_case_call(c, ast, &special)) return special; AST_GET_CHILDREN(ast, positional, named, postfix); AST_GET_CHILDREN(postfix, receiver, method); ast_t* typeargs = NULL; // Dig through function qualification. switch(ast_id(receiver)) { case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_BECHAIN: case TK_FUNCHAIN: typeargs = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Get the receiver type. const char* method_name = ast_name(method); ast_t* type = ast_type(receiver); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); // Generate the arguments. size_t count = ast_childcount(positional) + 1; size_t buf_size = count * sizeof(void*); LLVMValueRef* args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); ast_t* arg = ast_child(positional); int i = 1; while(arg != NULL) { LLVMValueRef value = gen_expr(c, arg); if(value == NULL) { ponyint_pool_free_size(buf_size, args); return NULL; } args[i] = value; arg = ast_sibling(arg); i++; } bool is_new_call = false; // Generate the receiver. Must be done after the arguments because the args // could change things in the receiver expression that must be accounted for. if(call_needs_receiver(postfix, t)) { switch(ast_id(postfix)) { case TK_NEWREF: case TK_NEWBEREF: { call_tuple_indices_t tuple_indices = {NULL, 0, 4}; tuple_indices.data = (size_t*)ponyint_pool_alloc_size(4 * sizeof(size_t)); ast_t* current = ast; ast_t* parent = ast_parent(current); while((parent != NULL) && (ast_id(parent) != TK_ASSIGN) && (ast_id(parent) != TK_CALL)) { if(ast_id(parent) == TK_TUPLE) { size_t index = 0; ast_t* child = ast_child(parent); while(current != child) { ++index; child = ast_sibling(child); } tuple_indices_push(&tuple_indices, index); } current = parent; parent = ast_parent(current); } // If we're constructing an embed field, pass a pointer to the field // as the receiver. Otherwise, allocate an object. if((parent != NULL) && (ast_id(parent) == TK_ASSIGN)) { size_t index = 1; current = ast_childidx(parent, 1); while((ast_id(current) == TK_TUPLE) || (ast_id(current) == TK_SEQ)) { parent = current; if(ast_id(current) == TK_TUPLE) { // If there are no indices left, we're destructuring a tuple. // Errors in those cases have already been catched by the expr // pass. if(tuple_indices.count == 0) break; index = tuple_indices_pop(&tuple_indices); current = ast_childidx(parent, index); } else { current = ast_childlast(parent); } } if(ast_id(current) == TK_EMBEDREF) { args[0] = gen_fieldptr(c, current); set_descriptor(c, t, args[0]); } else { args[0] = gencall_alloc(c, t); } } else { args[0] = gencall_alloc(c, t); } is_new_call = true; ponyint_pool_free_size(tuple_indices.alloc * sizeof(size_t), tuple_indices.data); break; } case TK_BEREF: case TK_FUNREF: case TK_BECHAIN: case TK_FUNCHAIN: args[0] = gen_expr(c, receiver); break; default: pony_assert(0); return NULL; } } else { // Use a null for the receiver type. args[0] = LLVMConstNull(t->use_type); } // Static or virtual dispatch. token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, method_name, typeargs); LLVMValueRef func = dispatch_function(c, t, m, args[0]); bool is_message = false; if((ast_id(postfix) == TK_NEWBEREF) || (ast_id(postfix) == TK_BEREF) || (ast_id(postfix) == TK_BECHAIN)) { switch(t->underlying) { case TK_ACTOR: is_message = true; break; case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_INTERFACE: case TK_TRAIT: if(m->cap == TK_TAG) is_message = can_inline_message_send(t, m, method_name); break; default: {} } } // Cast the arguments to the parameter types. LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, params); arg = ast_child(positional); i = 1; LLVMValueRef r = NULL; if(is_message) { // If we're sending a message, trace and send here instead of calling the // sender to trace the most specific types possible. LLVMValueRef* cast_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); cast_args[0] = args[0]; while(arg != NULL) { cast_args[i] = gen_assign_cast(c, params[i], args[i], ast_type(arg)); arg = ast_sibling(arg); i++; } token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, method_name, typeargs); codegen_debugloc(c, ast); gen_send_message(c, m, args, cast_args, positional); codegen_debugloc(c, NULL); switch(ast_id(postfix)) { case TK_NEWREF: case TK_NEWBEREF: r = args[0]; break; default: r = c->none_instance; break; } ponyint_pool_free_size(buf_size, cast_args); } else { while(arg != NULL) { args[i] = gen_assign_cast(c, params[i], args[i], ast_type(arg)); arg = ast_sibling(arg); i++; } if(func != NULL) { // If we can error out and we have an invoke target, generate an invoke // instead of a call. codegen_debugloc(c, ast); if(ast_canerror(ast) && (c->frame->invoke_target != NULL)) r = invoke_fun(c, func, args, i, "", true); else r = codegen_call(c, func, args, i); if(is_new_call) { LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0); LLVMSetMetadataStr(r, "pony.newcall", md); } codegen_debugloc(c, NULL); } } // Class constructors return void, expression result is the receiver. if(((ast_id(postfix) == TK_NEWREF) || (ast_id(postfix) == TK_NEWBEREF)) && (t->underlying == TK_CLASS)) r = args[0]; // Chained methods forward their receiver. if((ast_id(postfix) == TK_BECHAIN) || (ast_id(postfix) == TK_FUNCHAIN)) r = args[0]; ponyint_pool_free_size(buf_size, args); ponyint_pool_free_size(buf_size, params); return r; }
void gen_send_message(compile_t* c, reach_method_t* m, LLVMValueRef orig_args[], LLVMValueRef cast_args[], ast_t* args_ast) { // Allocate the message, setting its size and ID. size_t msg_size = (size_t)LLVMABISizeOfType(c->target_data, m->msg_type); LLVMTypeRef msg_type_ptr = LLVMPointerType(m->msg_type, 0); LLVMValueRef msg_args[3]; msg_args[0] = LLVMConstInt(c->i32, ponyint_pool_index(msg_size), false); msg_args[1] = LLVMConstInt(c->i32, m->vtable_index, false); LLVMValueRef msg = gencall_runtime(c, "pony_alloc_msg", msg_args, 2, ""); LLVMValueRef msg_ptr = LLVMBuildBitCast(c->builder, msg, msg_type_ptr, ""); for(unsigned int i = 0; i < m->param_count; i++) { LLVMValueRef arg_ptr = LLVMBuildStructGEP(c->builder, msg_ptr, i + 3, ""); LLVMBuildStore(c->builder, cast_args[i+1], arg_ptr); } // Trace while populating the message contents. ast_t* params = ast_childidx(m->r_fun, 3); ast_t* param = ast_child(params); ast_t* arg_ast = ast_child(args_ast); bool need_trace = false; while(param != NULL) { if(gentrace_needed(c, ast_type(arg_ast), ast_type(param))) { need_trace = true; break; } param = ast_sibling(param); arg_ast = ast_sibling(arg_ast); } LLVMValueRef ctx = codegen_ctx(c); if(need_trace) { gencall_runtime(c, "pony_gc_send", &ctx, 1, ""); param = ast_child(params); arg_ast = ast_child(args_ast); for(size_t i = 0; i < m->param_count; i++) { gentrace(c, ctx, orig_args[i+1], cast_args[i+1], ast_type(arg_ast), ast_type(param)); param = ast_sibling(param); arg_ast = ast_sibling(arg_ast); } gencall_runtime(c, "pony_send_done", &ctx, 1, ""); } // Send the message. msg_args[0] = ctx; msg_args[1] = LLVMBuildBitCast(c->builder, cast_args[0], c->object_ptr, ""); msg_args[2] = msg; gencall_runtime(c, "pony_sendv", msg_args, 3, ""); }
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err); bool err = (ast_id(can_err) == TK_QUESTION); // Get the function name, +1 to skip leading @ const char* f_name = ast_name(id) + 1; // Get the return type. ast_t* type = ast_type(ast); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); // Get the function. LLVMValueRef func = LLVMGetNamedFunction(c->module, f_name); if(func == NULL) { // If we have no prototype, declare one. ast_t* decl = (ast_t*)ast_data(ast); if(decl != NULL) { // Define using the declared types. AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_err); err = (ast_id(decl_err) == TK_QUESTION); func = declare_ffi(c, f_name, t, decl_params, err, false); } else if(!strncmp(f_name, "llvm.", 5)) { // Intrinsic, so use the exact types we supply. func = declare_ffi(c, f_name, t, args, err, true); } else { // Make it varargs. func = declare_ffi_vararg(c, f_name, t, err); } } // Generate the arguments. int count = (int)ast_childcount(args); size_t buf_size = count * sizeof(LLVMValueRef); LLVMValueRef* f_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* f_params = NULL; bool vararg = (LLVMIsFunctionVarArg(f_type) != 0); if(!vararg) { f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, f_params); } ast_t* arg = ast_child(args); for(int i = 0; i < count; i++) { f_args[i] = gen_expr(c, arg); if(!vararg) f_args[i] = cast_ffi_arg(c, f_args[i], f_params[i]); if(f_args[i] == NULL) { ponyint_pool_free_size(buf_size, f_args); return NULL; } arg = ast_sibling(arg); } // If we can error out and we have an invoke target, generate an invoke // instead of a call. LLVMValueRef result; codegen_debugloc(c, ast); if(err && (c->frame->invoke_target != NULL)) result = invoke_fun(c, func, f_args, count, "", false); else result = LLVMBuildCall(c->builder, func, f_args, count, ""); codegen_debugloc(c, NULL); ponyint_pool_free_size(buf_size, f_args); if(!vararg) ponyint_pool_free_size(buf_size, f_params); // Special case a None return value, which is used for void functions. if(is_none(type)) return t->instance; return result; }
static ast_t* lookup_nominal(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { assert(ast_id(type) == TK_NOMINAL); typecheck_t* t = &opt->check; ast_t* def = (ast_t*)ast_data(type); AST_GET_CHILDREN(def, type_id, typeparams); const char* type_name = ast_name(type_id); if((type_name[0] == '_') && (from != NULL) && (opt != NULL)) { if(ast_nearest(def, TK_PACKAGE) != t->frame->package) { if(errors) { ast_error(from, "can't lookup fields or methods on private types from other packages" ); } return NULL; } } ast_t* find = ast_get(def, name, NULL); if(find != NULL) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: break; case TK_NEW: case TK_BE: case TK_FUN: { // Typecheck default args immediately. if(opt != NULL) { AST_GET_CHILDREN(find, cap, id, typeparams, params); ast_t* param = ast_child(params); while(param != NULL) { AST_GET_CHILDREN(param, name, type, def_arg); if((ast_id(def_arg) != TK_NONE) && (ast_type(def_arg) == NULL)) { ast_settype(def_arg, ast_from(def_arg, TK_INFERTYPE)); if(ast_visit_scope(&def_arg, NULL, pass_expr, opt, PASS_EXPR) != AST_OK) return false; ast_visit_scope(&def_arg, NULL, pass_nodebug, opt, PASS_ALL); } param = ast_sibling(param); } } break; } default: find = NULL; } } if(find == NULL) { if(errors) ast_error(from, "couldn't find '%s' in '%s'", name, type_name); return NULL; } if((name[0] == '_') && (from != NULL) && (opt != NULL)) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: if(t->frame->type != def) { if(errors) { ast_error(from, "can't lookup private fields from outside the type"); } return NULL; } break; case TK_NEW: case TK_BE: case TK_FUN: { if(ast_nearest(def, TK_PACKAGE) != t->frame->package) { if(errors) { ast_error(from, "can't lookup private methods from outside the package"); } return NULL; } break; } default: assert(0); return NULL; } if(!strcmp(name, "_final")) { switch(ast_id(find)) { case TK_NEW: case TK_BE: case TK_FUN: if(errors) ast_error(from, "can't lookup a _final function"); return NULL; default: {} } } } ast_t* typeargs = ast_childidx(type, 2); ast_t* r_find = viewpoint_replacethis(find, orig); ast_t* rr_find = reify(r_find, typeparams, typeargs); ast_free_unattached(r_find); return rr_find; }
static bool compare_asts(ast_t* prev, ast_t* expected, ast_t* actual, bool check_siblings) { assert(prev != NULL); if(expected == NULL && actual == NULL) return true; if(actual == NULL) { ast_error(expected, "Expected AST %s not found", ast_get_print(expected)); return false; } if(expected == NULL) { ast_error(prev, "Unexpected AST node found, %s", ast_get_print(actual)); return false; } token_id expected_id = ast_id(expected); token_id actual_id = ast_id(actual); if(expected_id != actual_id) { ast_error(expected, "AST ID mismatch, got %d (%s), expected %d (%s)", ast_id(actual), ast_get_print(actual), ast_id(expected), ast_get_print(expected)); return false; } if(ast_id(expected) == TK_ID && ast_name(actual)[0] == '$' && strcmp(ast_name(expected), "hygid") == 0) { // Allow expected "hygid" to match any hygenic ID } else if(strcmp(ast_get_print(expected), ast_get_print(actual)) != 0) { ast_error(expected, "AST text mismatch, got %s, expected %s", ast_get_print(actual), ast_get_print(expected)); return false; } if(ast_has_scope(expected) && !ast_has_scope(actual)) { ast_error(expected, "AST missing scope"); return false; } if(!ast_has_scope(expected) && ast_has_scope(actual)) { ast_error(actual, "Unexpected AST scope"); return false; } if(!compare_asts(expected, ast_child(expected), ast_child(actual), true) || !compare_asts(expected, ast_type(expected), ast_type(actual), true)) return false; return !check_siblings || compare_asts(expected, ast_sibling(expected), ast_sibling(actual), 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; }
// Process the given capture and create the AST for the corresponding field. // Returns the create field AST, which must be freed by the caller. // Returns NULL on error. static ast_t* make_capture_field(pass_opt_t* opt, ast_t* capture) { assert(capture != NULL); AST_GET_CHILDREN(capture, id_node, type, value); const char* name = ast_name(id_node); // There are 3 varieties of capture: // x -> capture variable x, type from defn of x // x = y -> capture expression y, type inferred from expression type // x: T = y -> capture expression y, type T if(ast_id(value) == TK_NONE) { // Variable capture assert(ast_id(type) == TK_NONE); ast_t* def = ast_get(capture, name, NULL); if(def == NULL) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", variable not defined", name); return NULL; } token_id def_id = ast_id(def); if(def_id != TK_ID && def_id != TK_FVAR && def_id != TK_FLET && def_id != TK_PARAM) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } BUILD(capture_rhs, id_node, NODE(TK_REFERENCE, ID(name))); type = alias(ast_type(def)); value = capture_rhs; } else if(ast_id(type) == TK_NONE) { // No type specified, use type of the captured expression type = alias(ast_type(value)); } else { // Type given, infer literals if(!coerce_literals(&value, type, opt)) return NULL; } if(is_typecheck_error(type)) return NULL; type = sanitise_type(type); BUILD(field, id_node, NODE(TK_FVAR, TREE(id_node) TREE(type) TREE(value) NONE)); // Delegate type return field; }
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name, reach_type_t* t, ast_t* args, bool err, 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, err); 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); while(arg != NULL) { ast_t* p_type = ast_type(arg); if(p_type == NULL) p_type = ast_childidx(arg, 1); reach_type_t* pt = reach_type(c->reach, p_type); pony_assert(pt != NULL); // An intrinsic that takes a Bool should be i1, not ibool. if(intrinsic && is_bool(pt->ast)) f_params[count++] = c->i1; else f_params[count++] = pt->use_type; arg = ast_sibling(arg); } LLVMTypeRef r_type; if(t->underlying == TK_TUPLETYPE) { // Can't use the named type. Build an unnamed type with the same // elements. unsigned int count = LLVMCountStructElementTypes(t->use_type); size_t buf_size = count * sizeof(LLVMTypeRef); LLVMTypeRef* e_types = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetStructElementTypes(t->use_type, e_types); if(intrinsic) { ast_t* child = ast_child(t->ast); size_t i = 0; while(child != NULL) { // A Bool in an intrinsic tuple return type is an i1, not an ibool. if(is_bool(child)) e_types[i] = c->i1; child = ast_sibling(child); i++; } } r_type = LLVMStructTypeInContext(c->context, e_types, count, false); ponyint_pool_free_size(buf_size, e_types); } else { // An intrinsic that returns a Bool returns an i1, not an ibool. if(intrinsic && is_bool(t->ast)) r_type = c->i1; else r_type = t->use_type; } LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false); LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type); if(!err) { #if PONY_LLVM >= 309 LLVM_DECLARE_ATTRIBUTEREF(nounwind_attr, nounwind, 0); LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, nounwind_attr); #else LLVMAddFunctionAttr(func, LLVMNoUnwindAttribute); #endif } ponyint_pool_free_size(buf_size, f_params); return func; }
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; }
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); } }
bool expr_fun(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; AST_GET_CHILDREN(ast, cap, id, typeparams, params, type, can_error, body); if(ast_id(body) == TK_NONE) return true; if(!coerce_literals(&body, type, opt)) return false; bool is_trait = (ast_id(t->frame->type) == TK_TRAIT) || (ast_id(t->frame->type) == TK_INTERFACE) || (ast_id((ast_t*)ast_data(ast)) == TK_TRAIT) || (ast_id((ast_t*)ast_data(ast)) == TK_INTERFACE); // Check partial functions. if(ast_id(can_error) == TK_QUESTION) { // If a partial function, check that we might actually error. ast_t* body_type = ast_type(body); if(body_type == NULL) { // An error has already occurred. assert(get_error_count() > 0); return false; } if(!is_trait && !ast_canerror(body) && (ast_id(body_type) != TK_COMPILE_INTRINSIC)) { ast_error(can_error, "function body is not partial but the function is"); return false; } } else { // If not a partial function, check that we can't error. if(ast_canerror(body)) { ast_error(can_error, "function body is partial but the function is not"); show_partiality(body); return false; } } if(!check_primitive_init(t, ast) || !check_finaliser(t, ast)) return false; switch(ast_id(ast)) { case TK_NEW: { bool ok = true; if(is_machine_word(type)) { if(!check_return_type(ast)) ok = false; } if(!check_fields_defined(ast)) ok = false; if(!check_main_create(t, ast)) ok = false; return ok; } case TK_FUN: return check_return_type(ast); default: {} } return true; }
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); } }
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; }
LLVMValueRef make_divmod(compile_t* c, ast_t* left, ast_t* right, const_binop const_f, const_binop const_ui, const_binop const_si, build_binop build_f, build_binop build_ui, build_binop build_si) { ast_t* type = ast_type(left); bool sign = is_signed(c->opt, type); LLVMValueRef l_value = gen_expr(c, left); LLVMValueRef r_value = gen_expr(c, right); if((l_value == NULL) || (r_value == NULL)) return NULL; if(!is_fp(r_value) && LLVMIsConstant(r_value) && (LLVMConstIntGetSExtValue(r_value) == 0) ) { ast_error(right, "constant divide or mod by zero"); return NULL; } if(LLVMIsConstant(l_value) && LLVMIsConstant(r_value)) { if(is_fp(l_value)) return const_f(l_value, r_value); if(sign) return const_si(l_value, r_value); return const_ui(l_value, r_value); } if(is_fp(l_value)) return build_f(c->builder, l_value, r_value, ""); // Setup additional blocks. LLVMBasicBlockRef insert = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef then_block = codegen_block(c, "div_then"); LLVMBasicBlockRef post_block = codegen_block(c, "div_post"); // Check for div by zero. LLVMTypeRef r_type = LLVMTypeOf(r_value); LLVMValueRef zero = LLVMConstInt(r_type, 0, false); LLVMValueRef cmp = LLVMBuildICmp(c->builder, LLVMIntNE, r_value, zero, ""); LLVMBuildCondBr(c->builder, cmp, then_block, post_block); // Divisor is not zero. LLVMPositionBuilderAtEnd(c->builder, then_block); LLVMValueRef result; if(sign) result = build_si(c->builder, l_value, r_value, ""); else result = build_ui(c->builder, l_value, r_value, ""); LLVMBuildBr(c->builder, post_block); // Phi node. LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, r_type, ""); LLVMAddIncoming(phi, &zero, &insert, 1); LLVMAddIncoming(phi, &result, &then_block, 1); return phi; }
// Assign a UIF type from the given target type to the given AST static bool uif_type_from_chain(pass_opt_t* opt, ast_t* literal, ast_t* target_type, lit_chain_t* chain, bool require_float, bool report_errors) { assert(literal != NULL); assert(chain != NULL); lit_chain_t* chain_head = chain; while(chain_head->cardinality != CHAIN_CARD_BASE) chain_head = chain_head->next; if(chain_head->cached_type == NULL) { // This is the first time we've needed this type, find it if(!uif_type(opt, literal, target_type, chain_head, report_errors)) return false; } if(require_float && !chain_head->valid_for_float) { if(report_errors) ast_error(literal, "Inferred possibly integer type %s for float literal", chain_head->name); return false; } if(ast_id(literal) == TK_INT && chain_head->cached_uif_index >= 0) { // Check for literals that are outside the range of their type. // Note we don't check for types bound to type parameters. int i = chain_head->cached_uif_index; if(_str_uif_types[i].limit_low != 0 || _str_uif_types[i].limit_high != 0) { #ifdef PLATFORM_IS_VISUAL_STUDIO UnsignedInt128 limit(_str_uif_types[i].limit_high, _str_uif_types[i].limit_low); #else __uint128_t limit = (((__uint128_t)_str_uif_types[i].limit_high) << 64) | _str_uif_types[i].limit_low; #endif // There is a limit specified for this type, the literal must be smaller // than that. bool neg_plus_one = false; if(_str_uif_types[i].neg_plus_one) { // If the literal is immediately negated it can be equal to the given // limit. This is because of how the world chooses to encode negative // integers. // For example, the maximum value in an I8 is 127. But the minimum // value is -128. // We don't actually calculate the negative value here, but we have a // looser test if the literal is immediately negated. // We do not do this if the negation is not immediate, eg "-(128)". ast_t* parent = ast_parent(literal); assert(parent != NULL); ast_t* parent_type = ast_type(parent); if(parent_type != NULL && ast_id(parent_type) == TK_OPERATORLITERAL && ast_child(parent) == literal && ((lit_op_info_t*)ast_data(parent_type))->neg_plus_one) neg_plus_one = true; } __uint128_t actual = ast_int(literal); if((actual > limit) || (!neg_plus_one && actual == limit)) { // Illegal value. Note that we report an error, but don't return an // error, so other errors may be found. ast_error(literal, "Literal value is out of range for type (%s)", chain_head->name); } } } ast_settype(literal, chain_head->cached_type); return true; }
static bool check_arg_types(pass_opt_t* opt, ast_t* params, ast_t* positional, bool incomplete, bool partial) { // Check positional args vs params. ast_t* param = ast_child(params); ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) == TK_NONE) { if(partial) { // Don't check missing arguments for partial application. arg = ast_sibling(arg); param = ast_sibling(param); continue; } else { // Pick up a default argument if we can. if(!apply_default_arg(opt, param, arg)) return false; } } ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return false; ast_t* arg_type = ast_type(arg); if(is_typecheck_error(arg_type)) return false; if(is_control_type(arg_type)) { ast_error(opt->check.errors, arg, "can't use a control expression in an argument"); return false; } ast_t* a_type = alias(arg_type); if(incomplete) { ast_t* expr = arg; if(ast_id(arg) == TK_SEQ) expr = ast_child(arg); // If 'this' is incomplete and the arg is 'this', change the type to tag. if((ast_id(expr) == TK_THIS) && (ast_sibling(expr) == NULL)) { ast_t* tag_type = set_cap_and_ephemeral(a_type, TK_TAG, TK_NONE); ast_free_unattached(a_type); a_type = tag_type; } } errorframe_t info = NULL; if(!is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); arg = ast_sibling(arg); param = ast_sibling(param); } return true; }
bool literal_call(ast_t* ast, pass_opt_t* options) { assert(ast != NULL); assert(ast_id(ast) == TK_CALL); AST_GET_CHILDREN(ast, positional_args, named_args, receiver); ast_t* recv_type = ast_type(receiver); if(is_typecheck_error(recv_type)) return false; if(ast_id(recv_type) == TK_LITERAL) { ast_error(ast, "Cannot call a literal"); return false; } if(ast_id(recv_type) != TK_OPERATORLITERAL) // Nothing to do return true; lit_op_info_t* op = (lit_op_info_t*)ast_data(recv_type); assert(op != NULL); if(ast_childcount(named_args) != 0) { ast_error(named_args, "Cannot use named arguments with literal operator"); return false; } ast_t* arg = ast_child(positional_args); if(arg != NULL) { if(!unify(arg, options, true)) return false; ast_t* arg_type = ast_type(arg); if(is_typecheck_error(arg_type)) return false; if(ast_id(arg_type) != TK_LITERAL) // Apply argument type to receiver return coerce_literals(&receiver, arg_type, options); if(!op->can_propogate_literal) { ast_error(ast, "Cannot infer operand type"); return false; } } size_t arg_count = ast_childcount(positional_args); if(op->arg_count != arg_count) { ast_error(ast, "Invalid number of arguments to literal operator"); return false; } make_literal_type(ast); return true; }
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; }
bool expr_qualify(pass_opt_t* opt, ast_t** astp) { // Left is a postfix expression, right is a typeargs. ast_t* ast = *astp; AST_GET_CHILDREN(ast, left, right); ast_t* type = ast_type(left); assert(ast_id(right) == TK_TYPEARGS); if(is_typecheck_error(type)) return false; switch(ast_id(left)) { case TK_TYPEREF: { // Qualify the type. assert(ast_id(type) == TK_NOMINAL); // If the type isn't polymorphic or the type is already qualified, // sugar .apply(). ast_t* def = names_def(opt, type); ast_t* typeparams = ast_childidx(def, 1); if((ast_id(typeparams) == TK_NONE) || (ast_id(ast_childidx(type, 2)) != TK_NONE)) { if(!expr_nominal(opt, &type)) return false; break; } type = ast_dup(type); ast_t* typeargs = ast_childidx(type, 2); ast_replace(&typeargs, right); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: { // Qualify the function. assert(ast_id(type) == TK_FUNTYPE); ast_t* typeparams = ast_childidx(type, 1); if(!reify_defaults(typeparams, right, true, opt)) return false; if(!check_constraints(left, typeparams, right, true, opt)) return false; type = reify(type, typeparams, right, opt); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); ast_settype(ast, type); ast_setid(ast, ast_id(left)); ast_inheritflags(ast); return true; } default: {} } // Sugar .apply() ast_t* dot = ast_from(left, TK_DOT); ast_add(dot, ast_from_string(left, "apply")); ast_swap(left, dot); ast_add(dot, left); if(!expr_dot(opt, &dot)) return false; return expr_qualify(opt, astp); }
// 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); }