LLVMValueRef gen_match(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); ast_t* type = ast_type(ast); AST_GET_CHILDREN(ast, match_expr, cases, else_expr); // We will have no type if all case have control types. LLVMTypeRef phi_type = NULL; if(needed && !is_control_type(type)) { reach_type_t* t_phi = reach_type(c->reach, type); phi_type = t_phi->use_type; } ast_t* match_type = alias(ast_type(match_expr)); LLVMValueRef match_value = gen_expr(c, match_expr); LLVMBasicBlockRef pattern_block = codegen_block(c, "case_pattern"); LLVMBasicBlockRef else_block = codegen_block(c, "match_else"); LLVMBasicBlockRef post_block = NULL; LLVMBasicBlockRef next_block = NULL; // Jump to the first case. LLVMBuildBr(c->builder, pattern_block); LLVMValueRef phi = GEN_NOVALUE; if(!is_control_type(type)) { // Start the post block so that a case can modify the phi node. post_block = codegen_block(c, "match_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type, ""); else phi = GEN_NOTNEEDED; } // Iterate over the cases. ast_t* the_case = ast_child(cases); while(the_case != NULL) { ast_t* next_case = ast_sibling(the_case); if(next_case != NULL) next_block = codegen_block(c, "case_pattern"); else next_block = else_block; AST_GET_CHILDREN(the_case, pattern, guard, body); LLVMPositionBuilderAtEnd(c->builder, pattern_block); codegen_pushscope(c, the_case); ast_t* pattern_type = ast_type(the_case); bool ok = true; if(is_matchtype(match_type, pattern_type, c->opt) != MATCHTYPE_ACCEPT) { // If there's no possible match, jump directly to the next block. LLVMBuildBr(c->builder, next_block); } else { // Check the pattern. ok = static_match(c, match_value, match_type, pattern, next_block); // Check the guard. ok = ok && guard_match(c, guard, next_block); // Case body. ok = ok && case_body(c, body, post_block, phi, phi_type); } codegen_popscope(c); if(!ok) { ast_free_unattached(match_type); return NULL; } the_case = next_case; pattern_block = next_block; } ast_free_unattached(match_type); // Else body. LLVMPositionBuilderAtEnd(c->builder, else_block); codegen_pushscope(c, else_expr); bool ok = case_body(c, else_expr, post_block, phi, phi_type); codegen_popscope(c); if(!ok) return NULL; if(post_block != NULL) LLVMPositionBuilderAtEnd(c->builder, post_block); return phi; }
bool literal_call(ast_t* ast, pass_opt_t* opt) { pony_assert(ast != NULL); pony_assert(ast_id(ast) == TK_CALL); AST_GET_CHILDREN(ast, receiver, positional_args, named_args, question); ast_t* recv_type = ast_type(receiver); if(is_typecheck_error(recv_type)) return false; if(ast_id(recv_type) == TK_LITERAL) { ast_error(opt->check.errors, ast, "Cannot call a literal"); return false; } if(ast_id(recv_type) != TK_OPERATORLITERAL) // Nothing to do return true; lit_op_info_t const* op = (lit_op_info_t const*)ast_data(recv_type); pony_assert(op != NULL); if(ast_childcount(named_args) != 0) { ast_error(opt->check.errors, named_args, "Cannot use named arguments with literal operator"); return false; } ast_t* arg = ast_child(positional_args); if(arg != NULL) { if(!unify(arg, opt, 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, opt); if(!op->can_propogate_literal) { ast_error(opt->check.errors, ast, "Cannot infer operand type"); return false; } } size_t arg_count = ast_childcount(positional_args); if(op->arg_count != arg_count) { ast_error(opt->check.errors, ast, "Invalid number of arguments to literal operator"); return false; } make_literal_type(ast); return true; }
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; // Generate the return type. ast_t* type = ast_type(ast); gentype_t g; // Emit dwarf location of ffi call dwarf_location(&c->dwarf, ast); if(!gentype(c, type, &g)) return 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, &g, decl_params, err); } else if(!strncmp(f_name, "llvm.", 5)) { // Intrinsic, so use the exact types we supply. func = declare_ffi(c, f_name, &g, args, err); } else { // Make it varargs. func = declare_ffi_vararg(c, f_name, &g, 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 && (LLVMGetTypeKind(f_params[i]) == LLVMPointerTypeKind)) { if(LLVMGetTypeKind(LLVMTypeOf(f_args[i])) == LLVMIntegerTypeKind) f_args[i] = LLVMBuildIntToPtr(c->builder, f_args[i], f_params[i], ""); else f_args[i] = LLVMBuildBitCast(c->builder, 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; 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, ""); 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 g.instance; return result; }
static bool constructor_type(ast_t* ast, token_id cap, ast_t* type, ast_t** resultp) { switch(ast_id(type)) { case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(type); switch(ast_id(def)) { case TK_PRIMITIVE: case TK_CLASS: ast_setid(ast, TK_NEWREF); break; case TK_ACTOR: ast_setid(ast, TK_NEWBEREF); break; case TK_TYPE: ast_error(ast, "can't call a constructor on a type alias: %s", ast_print_type(type)); return false; case TK_INTERFACE: ast_error(ast, "can't call a constructor on an interface: %s", ast_print_type(type)); return false; case TK_TRAIT: ast_error(ast, "can't call a constructor on a trait: %s", ast_print_type(type)); return false; default: assert(0); return false; } return true; } case TK_TYPEPARAMREF: { // Alter the return type of the method. type = ast_dup(type); AST_GET_CHILDREN(type, tid, tcap, teph); ast_setid(tcap, cap); ast_setid(teph, TK_EPHEMERAL); ast_replace(resultp, type); // This could this be an actor. ast_setid(ast, TK_NEWBEREF); return true; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); return constructor_type(ast, cap, right, resultp); } default: {} } assert(0); return false; }
bool expr_try(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, body, else_clause, then_clause); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); ast_t* then_type = ast_type(then_clause); if(is_typecheck_error(body_type) || is_typecheck_error(else_type) || is_typecheck_error(then_type)) return false; // It has to be possible for the left side to result in an error. if((ast_id(ast) == TK_TRY) && !ast_canerror(body)) { ast_error(body, "try expression never results in an error"); return false; } ast_t* type = NULL; if(!is_control_type(body_type)) type = control_type_add_branch(type, body); if(!is_control_type(else_type)) type = control_type_add_branch(type, else_clause); if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_TRY); } // The then clause does not affect the type of the expression. if(is_control_type(then_type)) { ast_error(then_clause, "then clause always terminates the function"); return false; } if(is_type_literal(then_type)) { ast_error(then_clause, "Cannot infer type of unused literal"); return false; } ast_settype(ast, type); // Doesn't inherit error from the body. if(ast_canerror(else_clause) || ast_canerror(then_clause)) ast_seterror(ast); if(ast_cansend(body) || ast_cansend(else_clause) || ast_cansend(then_clause)) ast_setsend(ast); if(ast_mightsend(body) || ast_mightsend(else_clause) || ast_mightsend(then_clause)) ast_setmightsend(ast); literal_unify_control(ast, opt); // Push the symbol status from the then clause to our parent scope. ast_inheritstatus(ast_parent(ast), then_clause); return true; }
static bool is_valid_pattern(pass_opt_t* opt, ast_t* pattern) { if(ast_id(pattern) == TK_NONE) { ast_settype(pattern, ast_from(pattern, TK_DONTCARE)); return true; } ast_t* pattern_type = ast_type(pattern); if(is_control_type(pattern_type)) { ast_error(pattern, "not a matchable pattern"); return false; } switch(ast_id(pattern)) { case TK_VAR: case TK_LET: { // Disallow capturing tuples. AST_GET_CHILDREN(pattern, id, capture_type); if(ast_id(capture_type) == TK_TUPLETYPE) { ast_error(capture_type, "can't capture a tuple, change this into a tuple of capture " "expressions"); return false; } // Set the pattern type to be the capture type. ast_settype(pattern, capture_type); return true; } case TK_TUPLE: { ast_t* pattern_child = ast_child(pattern); // Treat a one element tuple as a normal expression. if(ast_sibling(pattern_child) == NULL) { bool ok = is_valid_pattern(opt, pattern_child); ast_settype(pattern, ast_type(pattern_child)); return ok; } // Check every element pairwise. ast_t* pattern_type = ast_from(pattern, TK_TUPLETYPE); bool ok = true; while(pattern_child != NULL) { if(!is_valid_pattern(opt, pattern_child)) ok = false; ast_append(pattern_type, ast_type(pattern_child)); pattern_child = ast_sibling(pattern_child); } ast_settype(pattern, pattern_type); return ok; } case TK_SEQ: { // Patterns cannot contain sequences. ast_t* child = ast_child(pattern); ast_t* next = ast_sibling(child); if(next != NULL) { ast_error(next, "expression in patterns cannot be sequences"); return false; } bool ok = is_valid_pattern(opt, child); ast_settype(pattern, ast_type(child)); return ok; } case TK_DONTCARE: // It's always ok not to care. return true; default: { // Structural equality, pattern.eq(match). ast_t* fun = lookup(opt, pattern, pattern_type, stringtab("eq")); if(fun == NULL) { ast_error(pattern, "this pattern element doesn't support structural equality"); return false; } if(ast_id(fun) != TK_FUN) { ast_error(pattern, "eq is not a function on this pattern element"); ast_error(fun, "definition of eq is here"); ast_free_unattached(fun); return false; } AST_GET_CHILDREN(fun, cap, id, typeparams, params, result, partial); bool ok = true; if(ast_id(typeparams) != TK_NONE) { ast_error(pattern, "polymorphic eq not supported in pattern matching"); ok = false; } if(!is_bool(result)) { ast_error(pattern, "eq must return Bool when pattern matching"); ok = false; } if(ast_id(partial) != TK_NONE) { ast_error(pattern, "eq cannot be partial when pattern matching"); ok = false; } ast_t* param = ast_child(params); if(param == NULL || ast_sibling(param) != NULL) { ast_error(pattern, "eq must take a single argument when pattern matching"); ok = false; } else { AST_GET_CHILDREN(param, param_id, param_type); ast_settype(pattern, param_type); } ast_free_unattached(fun); return ok; } } assert(0); return false; }
bool expr_case(pass_opt_t* opt, ast_t* ast) { assert(opt != NULL); assert(ast_id(ast) == TK_CASE); AST_GET_CHILDREN(ast, pattern, guard, body); if((ast_id(pattern) == TK_NONE) && (ast_id(guard) == TK_NONE)) { ast_error(ast, "can't have a case with no conditions, use an else clause"); return false; } ast_t* cases = ast_parent(ast); ast_t* match = ast_parent(cases); ast_t* match_expr = ast_child(match); ast_t* match_type = ast_type(match_expr); if(is_control_type(match_type) || is_typecheck_error(match_type)) return false; if(!infer_pattern_type(pattern, match_type, opt)) return false; if(!is_valid_pattern(opt, pattern)) return false; ast_t* operand_type = alias(match_type); ast_t* pattern_type = ast_type(pattern); bool ok = true; switch(is_matchtype(operand_type, pattern_type)) { case MATCHTYPE_ACCEPT: break; case MATCHTYPE_REJECT: ast_error(pattern, "this pattern can never match"); ast_error(match_type, "match type: %s", ast_print_type(operand_type)); ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; case MATCHTYPE_DENY: ast_error(pattern, "this capture violates capabilities"); ast_error(match_type, "match type: %s", ast_print_type(operand_type)); ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; } if(ast_id(guard) != TK_NONE) { ast_t* guard_type = ast_type(guard); if(is_typecheck_error(guard_type)) { ok = false; } else if(!is_bool(guard_type)) { ast_error(guard, "guard must be a boolean expression"); ok = false; } } ast_free_unattached(operand_type); ast_inheritflags(ast); return ok; }
static bool special_case_operator(compile_t* c, ast_t* ast, LLVMValueRef *value, bool short_circuit, bool native128) { AST_GET_CHILDREN(ast, postfix, positional, named, question); AST_GET_CHILDREN(postfix, left, method); ast_t* right = ast_child(positional); const char* name = ast_name(method); bool special_case = true; *value = NULL; codegen_debugloc(c, ast); if(name == c->str_add) *value = gen_add(c, left, right, true); else if(name == c->str_sub) *value = gen_sub(c, left, right, true); else if((name == c->str_mul) && native128) *value = gen_mul(c, left, right, true); else if((name == c->str_div) && native128) *value = gen_div(c, left, right, true); else if((name == c->str_rem) && native128) *value = gen_rem(c, left, right, true); else if(name == c->str_neg) *value = gen_neg(c, left, true); else if(name == c->str_add_unsafe) *value = gen_add(c, left, right, false); else if(name == c->str_sub_unsafe) *value = gen_sub(c, left, right, false); else if((name == c->str_mul_unsafe) && native128) *value = gen_mul(c, left, right, false); else if((name == c->str_div_unsafe) && native128) *value = gen_div(c, left, right, false); else if((name == c->str_rem_unsafe) && native128) *value = gen_rem(c, left, right, false); else if(name == c->str_neg_unsafe) *value = gen_neg(c, left, false); else if((name == c->str_and) && short_circuit) *value = gen_and_sc(c, left, right); else if((name == c->str_or) && short_circuit) *value = gen_or_sc(c, left, right); else if((name == c->str_and) && !short_circuit) *value = gen_and(c, left, right); else if((name == c->str_or) && !short_circuit) *value = gen_or(c, left, right); else if(name == c->str_xor) *value = gen_xor(c, left, right); else if(name == c->str_not) *value = gen_not(c, left); else if(name == c->str_shl) *value = gen_shl(c, left, right, true); else if(name == c->str_shr) *value = gen_shr(c, left, right, true); else if(name == c->str_shl_unsafe) *value = gen_shl(c, left, right, false); else if(name == c->str_shr_unsafe) *value = gen_shr(c, left, right, false); else if(name == c->str_eq) *value = gen_eq(c, left, right, true); else if(name == c->str_ne) *value = gen_ne(c, left, right, true); else if(name == c->str_lt) *value = gen_lt(c, left, right, true); else if(name == c->str_le) *value = gen_le(c, left, right, true); else if(name == c->str_ge) *value = gen_ge(c, left, right, true); else if(name == c->str_gt) *value = gen_gt(c, left, right, true); else if(name == c->str_eq_unsafe) *value = gen_eq(c, left, right, false); else if(name == c->str_ne_unsafe) *value = gen_ne(c, left, right, false); else if(name == c->str_lt_unsafe) *value = gen_lt(c, left, right, false); else if(name == c->str_le_unsafe) *value = gen_le(c, left, right, false); else if(name == c->str_ge_unsafe) *value = gen_ge(c, left, right, false); else if(name == c->str_gt_unsafe) *value = gen_gt(c, left, right, false); else special_case = false; codegen_debugloc(c, NULL); return special_case; }
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err); bool err = (ast_id(can_err) == TK_QUESTION); // Get the function name, +1 to skip leading @ const char* f_name = ast_name(id) + 1; deferred_reification_t* reify = c->frame->reify; // Get the return type. ast_t* type = deferred_reify(reify, ast_type(ast), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); ast_free_unattached(type); // Get the function. First check if the name is in use by a global and error // if it's the case. ffi_decl_t* ffi_decl; bool is_func = false; LLVMValueRef func = LLVMGetNamedGlobal(c->module, f_name); if(func == NULL) { func = LLVMGetNamedFunction(c->module, f_name); is_func = true; } if(func == NULL) { // If we have no prototype, declare one. ast_t* decl = (ast_t*)ast_data(ast); if(decl != NULL) { // Define using the declared types. AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_err); err = (ast_id(decl_err) == TK_QUESTION); func = declare_ffi(c, f_name, t, decl_params, false); } else if(!strncmp(f_name, "llvm.", 5) || !strncmp(f_name, "internal.", 9)) { // Intrinsic, so use the exact types we supply. func = declare_ffi(c, f_name, t, args, true); } else { // Make it varargs. func = declare_ffi_vararg(c, f_name, t); } size_t index = HASHMAP_UNKNOWN; #ifndef PONY_NDEBUG ffi_decl_t k; k.func = func; ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index); pony_assert(ffi_decl == NULL); #endif ffi_decl = POOL_ALLOC(ffi_decl_t); ffi_decl->func = func; ffi_decl->decl = (decl != NULL) ? decl : ast; ffi_decls_putindex(&c->ffi_decls, ffi_decl, index); } else { ffi_decl_t k; k.func = func; size_t index = HASHMAP_UNKNOWN; ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index); if((ffi_decl == NULL) && (!is_func || LLVMHasMetadataStr(func, "pony.abi"))) { ast_error(c->opt->check.errors, ast, "cannot use '%s' as an FFI name: " "name is already in use by the internal ABI", f_name); return NULL; } pony_assert(is_func); } // Generate the arguments. int count = (int)ast_childcount(args); size_t buf_size = count * sizeof(LLVMValueRef); LLVMValueRef* f_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* f_params = NULL; bool vararg = (LLVMIsFunctionVarArg(f_type) != 0); if(!vararg) { if(count != (int)LLVMCountParamTypes(f_type)) { ast_error(c->opt->check.errors, ast, "conflicting declarations for FFI function: declarations have an " "incompatible number of parameters"); if(ffi_decl != NULL) ast_error_continue(c->opt->check.errors, ffi_decl->decl, "first " "declaration is here"); return NULL; } f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, f_params); } ast_t* arg = ast_child(args); for(int i = 0; i < count; i++) { f_args[i] = gen_expr(c, arg); if(!vararg) f_args[i] = cast_ffi_arg(c, ffi_decl, ast, f_args[i], f_params[i], "parameters"); if(f_args[i] == NULL) { ponyint_pool_free_size(buf_size, f_args); return NULL; } arg = ast_sibling(arg); } // If we can error out and we have an invoke target, generate an invoke // instead of a call. LLVMValueRef result; codegen_debugloc(c, ast); if(err && (c->frame->invoke_target != NULL)) result = invoke_fun(c, func, f_args, count, "", false); else result = LLVMBuildCall(c->builder, func, f_args, count, ""); codegen_debugloc(c, NULL); ponyint_pool_free_size(buf_size, f_args); if(!vararg) ponyint_pool_free_size(buf_size, f_params); compile_type_t* c_t = (compile_type_t*)t->c_type; // Special case a None return value, which is used for void functions. bool isnone = is_none(t->ast); bool isvoid = LLVMGetReturnType(f_type) == c->void_type; if(isnone && isvoid) { result = c_t->instance; } else if(isnone != isvoid) { report_ffi_type_err(c, ffi_decl, ast, "return values"); return NULL; } result = cast_ffi_arg(c, ffi_decl, ast, result, c_t->use_type, "return values"); result = gen_assign_cast(c, c_t->use_type, result, t->ast_cap); return result; }
static bool is_lvalue(typecheck_t* t, ast_t* ast, bool need_value) { switch(ast_id(ast)) { case TK_DONTCARE: // Can only assign to it if we don't need the value. return !need_value; case TK_VAR: case TK_LET: return assign_id(t, ast_child(ast), ast_id(ast) == TK_LET, need_value); case TK_VARREF: { ast_t* id = ast_child(ast); return assign_id(t, id, false, need_value); } case TK_LETREF: { ast_error(ast, "can't assign to a let local"); return false; } case TK_FVARREF: { AST_GET_CHILDREN(ast, left, right); if(ast_id(left) == TK_THIS) return assign_id(t, right, false, need_value); return true; } case TK_FLETREF: { AST_GET_CHILDREN(ast, left, right); if(ast_id(left) != TK_THIS) { ast_error(ast, "can't assign to a let field"); return false; } if(t->frame->loop_body != NULL) { ast_error(ast, "can't assign to a let field in a loop"); return false; } return assign_id(t, right, true, need_value); } case TK_EMBEDREF: { AST_GET_CHILDREN(ast, left, right); if(ast_id(left) != TK_THIS) { ast_error(ast, "can't assign to an embed field"); return false; } if(t->frame->loop_body != NULL) { ast_error(ast, "can't assign to an embed field in a loop"); return false; } return assign_id(t, right, true, need_value); } case TK_TUPLE: { // A tuple is an lvalue if every component expression is an lvalue. ast_t* child = ast_child(ast); while(child != NULL) { if(!is_lvalue(t, child, need_value)) return false; child = ast_sibling(child); } return true; } case TK_SEQ: { // A sequence is an lvalue if it has a single child that is an lvalue. // This is used because the components of a tuple are sequences. ast_t* child = ast_child(ast); if(ast_sibling(child) != NULL) return false; return is_lvalue(t, child, need_value); } default: {} } return false; }
bool safe_to_write(ast_t* ast, ast_t* type) { switch(ast_id(ast)) { case TK_VAR: case TK_LET: case TK_VARREF: case TK_DONTCARE: return true; case TK_FVARREF: case TK_FLETREF: case TK_EMBEDREF: { // If the ast is x.f, we need the type of x, which will be a nominal // type or an arrow type, since we were able to lookup a field on it. AST_GET_CHILDREN(ast, left, right); ast_t* l_type = ast_type(left); // Any viewpoint adapted type will not be safe to write to. if(ast_id(l_type) != TK_NOMINAL) return false; token_id l_cap = cap_single(l_type); // If the RHS is safe to write, we're done. if(safe_field_write(l_cap, type)) return true; // If the field type (without adaptation) is safe, then it's ok as // well. So iso.tag = ref should be allowed. ast_t* r_type = ast_type(right); return safe_field_write(l_cap, r_type); } case TK_TUPLE: { // At this point, we know these will be the same length. assert(ast_id(type) == TK_TUPLETYPE); ast_t* child = ast_child(ast); ast_t* type_child = ast_child(type); while(child != NULL) { if(!safe_to_write(child, type_child)) return false; child = ast_sibling(child); type_child = ast_sibling(type_child); } assert(type_child == NULL); return true; } case TK_SEQ: { // Occurs when there is a tuple on the left. Each child of the tuple will // be a sequence, but only sequences with a single writeable child are // valid. Other types won't appear here. return safe_to_write(ast_child(ast), type); } default: {} } assert(0); return false; }
bool expr_assign(pass_opt_t* opt, ast_t* ast) { // Left and right are swapped in the AST to make sure we type check the // right side before the left. Fetch them in the opposite order. assert(ast != NULL); AST_GET_CHILDREN(ast, right, left); ast_t* l_type = ast_type(left); if(!is_lvalue(&opt->check, left, is_result_needed(ast))) { ast_error(ast, "left side must be something that can be assigned to"); return false; } assert(l_type != NULL); if(!coerce_literals(&right, l_type, opt)) return false; ast_t* r_type = ast_type(right); if(is_typecheck_error(r_type)) return false; if(!infer_locals(left, r_type)) return false; // Inferring locals may have changed the left type. l_type = ast_type(left); // Assignment is based on the alias of the right hand side. ast_t* a_type = alias(r_type); if(!is_subtype(a_type, l_type, true)) { ast_error(ast, "right side must be a subtype of left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_error(l_type, "left side type: %s", ast_print_type(l_type)); ast_free_unattached(a_type); return false; } if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE)) { switch(ast_id(a_type)) { case TK_UNIONTYPE: ast_error(ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } bool ok_safe = safe_to_write(left, a_type); if(!ok_safe) { if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL && ast_id(ast_child(left)) == TK_THIS) { // We are writing to a field in this ast_t* fn = ast_nearest(left, TK_FUN); if(fn != NULL) { ast_t* iso = ast_child(fn); assert(iso != NULL); token_id iso_id = ast_id(iso); if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG) { ast_error(ast, "cannot write to a field in a %s function. If you are trying to change state in a function use fun ref", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(ast, "not safe to write right side to left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); // If it's an embedded field, check for a constructor result. if(ast_id(left) == TK_EMBEDREF) { if((ast_id(right) != TK_CALL) || (ast_id(ast_childidx(right, 2)) != TK_NEWREF)) { ast_error(ast, "an embedded field must be assigned using a constructor"); return false; } } ast_settype(ast, consume_type(l_type, TK_NONE)); ast_inheritflags(ast); return true; }
// Resolve the field delegate body to use for the given method, if any. // Return the body to use, NULL if none found or BODY_ERROR on error. static ast_t* resolve_delegate_body(ast_t* entity, ast_t* method, method_t* info, const char* name) { assert(entity != NULL); assert(method != NULL); assert(info != NULL); assert(name != NULL); if(info->delegate_field_2 != NULL) { // Ambiguous delegate assert(info->delegate_field_1 != NULL); assert(info->delegate_target_1 != NULL); assert(info->delegate_target_2 != NULL); ast_error(entity, "clashing delegates for method %s, local disambiguation required", name); ast_error(info->delegate_field_1, "field %s delegates to %s via %s", ast_name(ast_child(info->delegate_field_1)), name, ast_name(ast_child(info->delegate_target_1))); ast_error(info->delegate_field_2, "field %s delegates to %s via %s", ast_name(ast_child(info->delegate_field_2)), name, ast_name(ast_child(info->delegate_target_2))); return BODY_ERROR; } if(info->delegate_field_1 == NULL) // No delegation needed return NULL; // We have a delegation, make a redirection body const char* field_name = ast_name(ast_child(info->delegate_field_1)); ast_t* args = ast_from(info->delegate_field_1, TK_NONE); ast_t* last_arg = NULL; AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body); for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { const char* param_name = ast_name(ast_child(p)); BUILD(arg, info->delegate_field_1, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(param_name))))); ast_list_append(args, &last_arg, arg); ast_setid(args, TK_POSITIONALARGS); } BUILD(body, info->delegate_field_1, NODE(TK_SEQ, NODE(TK_CALL, TREE(args) // Positional args NODE(TK_NONE) // Named args NODE(TK_DOT, // Receiver NODE(TK_REFERENCE, ID(field_name)) ID(name))))); if(is_none(result)) { // Add None to end of body. Whilst the call generated above will return // None anyway in this case, without this extra None testing is very hard // since a directly written version of this body will have the None. BUILD(none, info->delegate_field_1, NODE(TK_REFERENCE, ID("None"))); ast_append(body, none); } info->body_donor = entity; return body; }
// Process all field delegations in the given type. // Stage 3. static bool field_delegations(ast_t* entity) { assert(entity != NULL); ast_t* members = ast_childidx(entity, 4); assert(members != NULL); bool r = true; // Check all fields for(ast_t* f = ast_child(members); f != NULL; f = ast_sibling(f)) { if(is_field(f)) { AST_GET_CHILDREN(f, id, f_type, value, delegates); // Check all delegates for field for(ast_t* d = ast_child(delegates); d != NULL; d = ast_sibling(d)) { if(!check_delegate(entity, f_type, d)) { r = false; continue; } // Mark all methods in delegate trait as targets for this field ast_t* trait = (ast_t*)ast_data(d); assert(trait != NULL); ast_t* t_members = ast_childidx(trait, 4); for(ast_t* m = ast_child(t_members); m != NULL; m = ast_sibling(m)) { if(is_method(m)) { // Mark method as delegate target for this field const char* method_name = ast_name(ast_childidx(m, 1)); assert(method_name != NULL); ast_t* local_method = ast_get(entity, method_name, NULL); assert(local_method != NULL); method_t* info = (method_t*)ast_data(local_method); assert(info != NULL); if(info->delegate_field_1 == NULL) { // First delegate field for this method info->delegate_field_1 = f; info->delegate_target_1 = d; } else if(info->delegate_field_2 == NULL && info->delegate_field_1 != f) { // We already have one delegate field, record second info->delegate_field_2 = f; info->delegate_target_2 = d; } } } } } } return r; }
LLVMValueRef gen_if(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); ast_t* type = ast_type(ast); AST_GET_CHILDREN(ast, cond, left, right); ast_t* left_type = ast_type(left); ast_t* right_type = ast_type(right); // We will have no type if both branches have return statements. reach_type_t* phi_type = NULL; if(!is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMValueRef c_value = gen_expr(c, cond); if(c_value == NULL) return NULL; // If the conditional is constant, generate only one branch. bool gen_left = true; bool gen_right = true; if(LLVMIsAConstantInt(c_value)) { int value = (int)LLVMConstIntGetZExtValue(c_value); if(value == 0) gen_left = false; else gen_right = false; } LLVMBasicBlockRef then_block = codegen_block(c, "if_then"); LLVMBasicBlockRef else_block = codegen_block(c, "if_else"); LLVMBasicBlockRef post_block = NULL; // If both branches return, we have no post block. if(!is_control_type(type)) post_block = codegen_block(c, "if_post"); LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, then_block, else_block); // Left branch. LLVMPositionBuilderAtEnd(c->builder, then_block); LLVMValueRef l_value; if(gen_left) { l_value = gen_expr(c, left); } else if(phi_type != NULL) { l_value = LLVMConstNull(phi_type->use_type); } else { LLVMBuildUnreachable(c->builder); l_value = GEN_NOVALUE; } if(l_value != GEN_NOVALUE) { if(needed) l_value = gen_assign_cast(c, phi_type->use_type, l_value, left_type); if(l_value == NULL) return NULL; then_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // Right branch. LLVMPositionBuilderAtEnd(c->builder, else_block); LLVMValueRef r_value; if(gen_right) { r_value = gen_expr(c, right); } else if(phi_type != NULL) { r_value = LLVMConstNull(phi_type->use_type); } else { LLVMBuildUnreachable(c->builder); r_value = GEN_NOVALUE; } // If the right side returns, we don't branch to the post block. if(r_value != GEN_NOVALUE) { if(needed) r_value = gen_assign_cast(c, phi_type->use_type, r_value, right_type); if(r_value == NULL) return NULL; else_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // If both sides return, we return a sentinal value. if(is_control_type(type)) return GEN_NOVALUE; // Continue in the post block. LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { LLVMValueRef phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); if(l_value != GEN_NOVALUE) LLVMAddIncoming(phi, &l_value, &then_block, 1); if(r_value != GEN_NOVALUE) LLVMAddIncoming(phi, &r_value, &else_block, 1); return phi; } return GEN_NOTNEEDED; }
static bool special_case_call(compile_t* c, ast_t* ast, LLVMValueRef* value) { AST_GET_CHILDREN(ast, postfix, positional, named, question); if((ast_id(postfix) != TK_FUNREF) || (ast_id(named) != TK_NONE)) return false; AST_GET_CHILDREN(postfix, receiver, method); ast_t* receiver_type = deferred_reify(c->frame->reify, ast_type(receiver), c->opt); const char* name = NULL; if(ast_id(receiver_type) == TK_NOMINAL) { AST_GET_CHILDREN(receiver_type, package, id); if(ast_name(package) == c->str_builtin) name = ast_name(id); } ast_free_unattached(receiver_type); if(name == NULL) return false; if(name == c->str_Bool) return special_case_operator(c, ast, value, true, true); if((name == c->str_I8) || (name == c->str_I16) || (name == c->str_I32) || (name == c->str_I64) || (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_ULong) || (name == c->str_USize) || (name == c->str_F32) || (name == c->str_F64) ) { return special_case_operator(c, ast, value, false, true); } if((name == c->str_I128) || (name == c->str_U128)) { bool native128 = target_is_native128(c->opt->triple); return special_case_operator(c, ast, value, false, native128); } if(name == c->str_Platform) { *value = special_case_platform(c, ast); return true; } return false; }
LLVMValueRef gen_try(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); AST_GET_CHILDREN(ast, body, else_clause, then_clause); ast_t* type = ast_type(ast); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); reach_type_t* phi_type = NULL; // We will have no type if both branches have return statements. if(!is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMBasicBlockRef block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef else_block = codegen_block(c, "try_else"); LLVMBasicBlockRef post_block = NULL; if(!is_control_type(type)) post_block = codegen_block(c, "try_post"); // Keep a reference to the else block. codegen_pushtry(c, else_block); // Body block. LLVMPositionBuilderAtEnd(c->builder, block); LLVMValueRef body_value = gen_expr(c, body); if(body_value != GEN_NOVALUE) { if(needed) { body_value = gen_assign_cast(c, phi_type->use_type, body_value, body_type); } if(body_value == NULL) return NULL; gen_expr(c, then_clause); block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // Pop the try before generating the else block. codegen_poptry(c); // Else block. LLVMPositionBuilderAtEnd(c->builder, else_block); // The landing pad is marked as a cleanup, since exceptions are typeless and // valueless. The first landing pad is always the destination. LLVMTypeRef lp_elements[2]; lp_elements[0] = c->void_ptr; lp_elements[1] = c->i32; LLVMTypeRef lp_type = LLVMStructTypeInContext(c->context, lp_elements, 2, false); #if PONY_LLVM == 307 && LLVM_VERSION_PATCH == 0 // This backwards-incompatible API change to LLVMBuildLandingPad is only in // LLVM 3.7.0. In 3.7.1 and all later versions, backward-compatibility was // restored. assert((c->frame->fun != NULL) && "No function in current frame!"); LLVMSetPersonalityFn(c->frame->fun, c->personality); LLVMValueRef landing = LLVMBuildLandingPad(c->builder, lp_type, 1, ""); #else LLVMValueRef landing = LLVMBuildLandingPad(c->builder, lp_type, c->personality, 1, ""); #endif LLVMAddClause(landing, LLVMConstNull(c->void_ptr)); LLVMValueRef else_value = gen_expr(c, else_clause); if(else_value != GEN_NOVALUE) { if(needed) { else_value = gen_assign_cast(c, phi_type->use_type, else_value, else_type); } if(else_value == NULL) return NULL; gen_expr(c, then_clause); else_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // If both sides return, we return a sentinal value. if(is_control_type(type)) return GEN_NOVALUE; // Continue in the post block. LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { LLVMValueRef phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); if(body_value != GEN_NOVALUE) LLVMAddIncoming(phi, &body_value, &block, 1); if(else_value != GEN_NOVALUE) LLVMAddIncoming(phi, &else_value, &else_block, 1); return phi; } return GEN_NOTNEEDED; }
LLVMValueRef gen_funptr(compile_t* c, ast_t* ast) { pony_assert((ast_id(ast) == TK_FUNREF) || (ast_id(ast) == TK_BEREF)); AST_GET_CHILDREN(ast, receiver, method); ast_t* typeargs = NULL; // Dig through function qualification. switch(ast_id(receiver)) { case TK_BEREF: case TK_FUNREF: typeargs = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Generate the receiver. LLVMValueRef value = gen_expr(c, receiver); // Get the receiver type. ast_t* type = deferred_reify(c->frame->reify, ast_type(receiver), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); const char* name = ast_name(method); token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, name, typeargs); LLVMValueRef funptr = dispatch_function(c, t, m, value); ast_free_unattached(type); if((m->cap != TK_AT) && (c->linkage != LLVMExternalLinkage)) { // We must reset the function linkage and calling convention since we're // passing a function pointer to a FFI call. Bare methods always use the // external linkage and the C calling convention so we don't need to process // them. switch(t->underlying) { case TK_PRIMITIVE: case TK_STRUCT: case TK_CLASS: case TK_ACTOR: { compile_method_t* c_m = (compile_method_t*)m->c_method; LLVMSetFunctionCallConv(c_m->func, LLVMCCallConv); LLVMSetLinkage(c_m->func, LLVMExternalLinkage); break; } case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_INTERFACE: case TK_TRAIT: set_method_external_interface(t, name, m->vtable_index); break; default: pony_assert(0); break; } } return funptr; }
bool expr_match(pass_opt_t* opt, ast_t* ast) { assert(ast_id(ast) == TK_MATCH); AST_GET_CHILDREN(ast, expr, cases, else_clause); // A literal match expression should have been caught by the cases, but check // again to avoid an assert if we've missed a case ast_t* expr_type = ast_type(expr); if(is_typecheck_error(expr_type)) return false; if(is_type_literal(expr_type)) { ast_error(expr, "cannot infer type for literal match expression"); return false; } ast_t* cases_type = ast_type(cases); ast_t* else_type = ast_type(else_clause); if(is_typecheck_error(cases_type) || is_typecheck_error(else_type)) return false; ast_t* type = NULL; size_t branch_count = 0; if(!is_control_type(cases_type)) { type = control_type_add_branch(type, cases); ast_inheritbranch(ast, cases); branch_count++; } if(!is_control_type(else_type)) { type = control_type_add_branch(type, else_clause); ast_inheritbranch(ast, else_clause); branch_count++; } if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_MATCH); } ast_settype(ast, type); ast_inheritflags(ast); ast_consolidate_branches(ast, branch_count); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
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, postfix, positional, named, question); AST_GET_CHILDREN(postfix, receiver, method); ast_t* typeargs = NULL; deferred_reification_t* reify = c->frame->reify; // 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 = deferred_reify(reify, method, c->opt); AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Get the receiver type. const char* method_name = ast_name(method); ast_t* type = deferred_reify(reify, ast_type(receiver), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, method_name, typeargs); ast_free_unattached(type); ast_free_unattached(typeargs); // Generate the arguments. size_t count = m->param_count + 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: args[0] = gen_constructor_receiver(c, t, ast); is_new_call = true; 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(((compile_type_t*)t->c_type)->use_type); } // Static or virtual dispatch. 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: {} } } bool bare = m->cap == TK_AT; 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. codegen_debugloc(c, ast); gen_send_message(c, m, 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; } } else { LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, params + (bare ? 1 : 0)); arg = ast_child(positional); i = 1; while(arg != NULL) { ast_t* arg_type = deferred_reify(reify, ast_type(arg), c->opt); args[i] = gen_assign_cast(c, params[i], args[i], arg_type); ast_free_unattached(arg_type); arg = ast_sibling(arg); i++; } uintptr_t arg_offset = 0; if(bare) { arg_offset = 1; 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 + arg_offset, i, "", !bare); else r = codegen_call(c, func, args + arg_offset, i, !bare); if(is_new_call) { LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0); LLVMSetMetadataStr(r, "pony.newcall", md); } codegen_debugloc(c, NULL); ponyint_pool_free_size(buf_size, params); } } // Bare methods with None return type return void, special case a None return // value. if(bare && is_none(m->result->ast)) r = c->none_instance; // 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); return r; }
static bool declared_ffi(pass_opt_t* opt, ast_t* call, ast_t* decl) { assert(call != NULL); assert(decl != NULL); assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(call, call_name, call_ret_typeargs, args, named_args, call_error); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); // Check args vs params ast_t* param = ast_child(params); ast_t* arg = ast_child(args); while((arg != NULL) && (param != NULL) && ast_id(param) != TK_ELLIPSIS) { ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return false; ast_t* a_type = ast_type(arg); errorframe_t info = NULL; if((a_type != NULL) && !void_star_param(p_type, a_type) && !is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return false; } arg = ast_sibling(arg); param = ast_sibling(param); } if(arg != NULL && param == NULL) { ast_error(opt->check.errors, arg, "too many arguments"); return false; } if(param != NULL && ast_id(param) != TK_ELLIPSIS) { ast_error(opt->check.errors, named_args, "too few arguments"); return false; } for(; arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return false; } } // Check return types ast_t* call_ret_type = ast_child(call_ret_typeargs); ast_t* decl_ret_type = ast_child(decl_ret_typeargs); errorframe_t info = NULL; if((call_ret_type != NULL) && !is_eqtype(call_ret_type, decl_ret_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, call_ret_type, "call return type does not match declaration"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return false; } // Store the declaration so that codegen can generate a non-variadic // signature for the FFI call. ast_setdata(call, decl); ast_settype(call, decl_ret_type); return true; }
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 = deferred_reify(c->frame->reify, ast_type(pattern), c->opt); 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) ) { ast_free_unattached(pattern_type); 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); ast_free_unattached(pattern_type); 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, true); codegen_debugloc(c, NULL); return result; }
bool expr_seq(pass_opt_t* opt, ast_t* ast) { bool ok = true; // Any expression other than the last that is still literal is an error for(ast_t* p = ast_child(ast); ast_sibling(p) != NULL; p = ast_sibling(p)) { ast_t* p_type = ast_type(p); if(is_typecheck_error(p_type)) { ok = false; } else if(is_type_literal(p_type)) { ast_error(p, "Cannot infer type of unused literal"); ok = false; } } if(ok) { // We might already have a type due to a return expression. ast_t* type = ast_type(ast); ast_t* last = ast_childlast(ast); if((type != NULL) && !coerce_literals(&last, type, opt)) return false; // Type is unioned with the type of the last child. type = control_type_add_branch(type, last); ast_settype(ast, type); ast_inheritflags(ast); } if(!ast_has_scope(ast)) return ok; ast_t* parent = ast_parent(ast); switch(ast_id(parent)) { case TK_TRY: case TK_TRY_NO_CHECK: { // Propagate consumes forward in a try expression. AST_GET_CHILDREN(parent, body, else_clause, then_clause); if(body == ast) { // Push our consumes, but not defines, to the else clause. ast_inheritbranch(else_clause, body); ast_consolidate_branches(else_clause, 2); } else if(else_clause == ast) { // Push our consumes, but not defines, to the then clause. This // includes the consumes from the body. ast_inheritbranch(then_clause, else_clause); ast_consolidate_branches(then_clause, 2); } } default: {} } return ok; }
static void print_type(printbuf_t* buffer, ast_t* type) { switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, package, id, typeargs, cap, ephemeral); ast_t* origpkg = ast_sibling(ephemeral); if(origpkg != NULL && ast_id(origpkg) != TK_NONE) printbuf(buffer, "%s.", ast_name(origpkg)); ast_t* def = (ast_t*)ast_data(type); if(def != NULL) id = ast_child(def); printbuf(buffer, "%s", ast_nice_name(id)); if(ast_id(typeargs) != TK_NONE) print_typeexpr(buffer, typeargs, ", ", true); if(ast_id(cap) != TK_NONE) printbuf(buffer, " %s", token_print(cap->t)); if(ast_id(ephemeral) != TK_NONE) printbuf(buffer, "%s", token_print(ephemeral->t)); break; } case TK_UNIONTYPE: print_typeexpr(buffer, type, " | ", false); break; case TK_ISECTTYPE: print_typeexpr(buffer, type, " & ", false); break; case TK_TUPLETYPE: print_typeexpr(buffer, type, ", ", false); break; case TK_TYPEPARAMREF: { AST_GET_CHILDREN(type, id, cap, ephemeral); printbuf(buffer, "%s", ast_nice_name(id)); if(ast_id(cap) != TK_NONE) printbuf(buffer, " %s", token_print(cap->t)); if(ast_id(ephemeral) != TK_NONE) printbuf(buffer, " %s", token_print(ephemeral->t)); break; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); print_type(buffer, left); printbuf(buffer, "->"); print_type(buffer, right); break; } case TK_THISTYPE: printbuf(buffer, "this"); break; case TK_DONTCARE: printbuf(buffer, "_"); break; case TK_FUNTYPE: printbuf(buffer, "function"); break; case TK_INFERTYPE: printbuf(buffer, "to_infer"); break; case TK_ERRORTYPE: printbuf(buffer, "<type error>"); break; case TK_NONE: break; default: printbuf(buffer, "%s", token_print(type->t)); } }
// 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* opt, bool report_errors) { pony_assert(astp != NULL); ast_t* literal_expr = *astp; pony_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, opt, 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, opt, report_errors)) return false; break; } case TK_INT: return uif_type_from_chain(opt, literal_expr, target_type, chain, false, report_errors); case TK_FLOAT: return uif_type_from_chain(opt, literal_expr, target_type, chain, true, report_errors); case TK_ARRAY: if(!coerce_group(astp, target_type, chain, CHAIN_CARD_ARRAY, opt, 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, opt, report_errors)) return false; ast_settype(literal_expr, ast_type(last)); return true; } case TK_CALL: { AST_GET_CHILDREN(literal_expr, receiver, positional, named, question); ast_t* arg = ast_child(positional); if(!coerce_literal_to_type(&receiver, target_type, chain, opt, report_errors)) return false; if(arg != NULL && !coerce_literal_to_type(&arg, target_type, chain, opt, 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, opt, report_errors)) return false; break; } case TK_RECOVER: { ast_t* expr = ast_childidx(literal_expr, 1); if(!coerce_literal_to_type(&expr, target_type, chain, opt, report_errors)) return false; break; } default: ast_error(opt->check.errors, literal_expr, "Internal error, coerce_literal_to_type node %s", ast_get_print(literal_expr)); pony_assert(0); return false; } // Need to reprocess node now all the literals have types ast_settype(literal_expr, NULL); return (pass_expr(astp, opt) == AST_OK); }
LLVMValueRef gen_while(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); AST_GET_CHILDREN(ast, cond, body, else_clause); ast_t* type = ast_type(ast); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); reach_type_t* phi_type = NULL; if(needed && !is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMBasicBlockRef init_block = codegen_block(c, "while_init"); LLVMBasicBlockRef body_block = codegen_block(c, "while_body"); LLVMBasicBlockRef else_block = codegen_block(c, "while_else"); LLVMBasicBlockRef post_block = NULL; LLVMBuildBr(c->builder, init_block); // start the post block so that a break can modify the phi node LLVMValueRef phi = GEN_NOTNEEDED; if(!is_control_type(type)) { // Start the post block so that a break can modify the phi node. post_block = codegen_block(c, "while_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); } // Push the loop status. codegen_pushloop(c, init_block, post_block, else_block); // init // This jumps either to the body or the else clause. This is not evaluated // on each loop iteration: only on the first entry or after a continue. LLVMPositionBuilderAtEnd(c->builder, init_block); LLVMValueRef i_value = gen_expr(c, cond); if(i_value == NULL) return NULL; LLVMValueRef test = LLVMBuildTrunc(c->builder, i_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, body_block, else_block); // Body. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef l_value = gen_expr(c, body); if(needed) l_value = gen_assign_cast(c, phi_type->use_type, l_value, body_type); if(l_value == NULL) return NULL; LLVMBasicBlockRef body_from = NULL; // If the body can't result in a value, don't generate the conditional // evaluation. This basic block for the body already has a terminator. if(l_value != GEN_NOVALUE) { // The body evaluates the condition itself, jumping either back to the body // or directly to the post block. LLVMValueRef c_value = gen_expr(c, cond); if(c_value == NULL) return NULL; body_from = LLVMGetInsertBlock(c->builder); LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); } // Don't need loop status for the else block. codegen_poploop(c); // else // If the loop doesn't generate a value (doesn't execute, or continues on the // last iteration), the else clause generates the value. LLVMPositionBuilderAtEnd(c->builder, else_block); LLVMValueRef r_value = gen_expr(c, else_clause); LLVMBasicBlockRef else_from = NULL; if(r_value != GEN_NOVALUE) { if(r_value == NULL) return NULL; if(needed) r_value = gen_assign_cast(c, phi_type->use_type, r_value, else_type); else_from = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } if(is_control_type(type)) return GEN_NOVALUE; // post LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { if(l_value != GEN_NOVALUE) LLVMAddIncoming(phi, &l_value, &body_from, 1); if(r_value != GEN_NOVALUE) LLVMAddIncoming(phi, &r_value, &else_from, 1); return phi; } return GEN_NOTNEEDED; }
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: typeargs = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Generate the receiver type. const char* method_name = ast_name(method); ast_t* type = ast_type(receiver); gentype_t g; if(!gentype(c, type, &g)) return NULL; // Generate the arguments. LLVMTypeRef f_type = genfun_sig(c, &g, method_name, typeargs); if(f_type == NULL) { ast_error(ast, "couldn't create a signature for '%s'", method_name); return NULL; } size_t count = ast_childcount(positional) + 1; size_t buf_size = count * sizeof(void*); LLVMValueRef* args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, params); ast_t* arg = ast_child(positional); int i = 1; while(arg != NULL) { LLVMValueRef value = make_arg(c, params[i], arg); if(value == NULL) { ponyint_pool_free_size(buf_size, args); ponyint_pool_free_size(buf_size, params); return NULL; } args[i] = value; arg = ast_sibling(arg); i++; } // 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, &g)) { switch(ast_id(postfix)) { case TK_NEWREF: case TK_NEWBEREF: { ast_t* parent = ast_parent(ast); ast_t* sibling = ast_sibling(ast); // If we're constructing an embed field, pass a pointer to the field // as the receiver. Otherwise, allocate an object. if((ast_id(parent) == TK_ASSIGN) && (ast_id(sibling) == TK_EMBEDREF)) args[0] = gen_fieldptr(c, sibling); else args[0] = gencall_alloc(c, &g); break; } case TK_BEREF: case TK_FUNREF: args[0] = gen_expr(c, receiver); break; default: assert(0); return NULL; } } else { // Use a null for the receiver type. args[0] = LLVMConstNull(g.use_type); } // Always emit location info for a call, to prevent inlining errors. This may // be disabled in dispatch_function, if the target function has no debug // info set. ast_setdebug(ast, true); dwarf_location(&c->dwarf, ast); // Static or virtual dispatch. LLVMValueRef func = dispatch_function(c, ast, &g, args[0], method_name, typeargs); LLVMValueRef r = NULL; if(func != NULL) { // If we can error out and we have an invoke target, generate an invoke // instead of a call. 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); } ponyint_pool_free_size(buf_size, args); ponyint_pool_free_size(buf_size, params); return r; }
LLVMValueRef gen_repeat(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); AST_GET_CHILDREN(ast, body, cond, else_clause); ast_t* type = ast_type(ast); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); reach_type_t* phi_type = NULL; if(needed && !is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMBasicBlockRef body_block = codegen_block(c, "repeat_body"); LLVMBasicBlockRef cond_block = codegen_block(c, "repeat_cond"); LLVMBasicBlockRef else_block = codegen_block(c, "repeat_else"); LLVMBasicBlockRef post_block = NULL; LLVMBuildBr(c->builder, body_block); // start the post block so that a break can modify the phi node LLVMValueRef phi = GEN_NOTNEEDED; if(!is_control_type(type)) { // Start the post block so that a break can modify the phi node. post_block = codegen_block(c, "repeat_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); } // Push the loop status. codegen_pushloop(c, cond_block, post_block, else_block); // Body. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef value = gen_expr(c, body); if(needed) value = gen_assign_cast(c, phi_type->use_type, value, body_type); if(value == NULL) return NULL; LLVMBasicBlockRef body_from = NULL; // If the body can't result in a value, don't generate the conditional // evaluation. This basic block for the body already has a terminator. if(value != GEN_NOVALUE) { // The body evaluates the condition itself, jumping either back to the body // or directly to the post block. LLVMValueRef c_value = gen_expr(c, cond); if(c_value == NULL) return NULL; body_from = LLVMGetInsertBlock(c->builder); LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, post_block, body_block); } // cond block // This is only evaluated from a continue, jumping either back to the body // or to the else block. LLVMPositionBuilderAtEnd(c->builder, cond_block); LLVMValueRef i_value = gen_expr(c, cond); LLVMValueRef test = LLVMBuildTrunc(c->builder, i_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, else_block, body_block); // Don't need loop status for the else block. codegen_poploop(c); // else // Only happens for a continue in the last iteration. LLVMPositionBuilderAtEnd(c->builder, else_block); LLVMValueRef else_value = gen_expr(c, else_clause); LLVMBasicBlockRef else_from = NULL; if(else_value == NULL) return NULL; if(needed) else_value = gen_assign_cast(c, phi_type->use_type, else_value, else_type); if(else_value != GEN_NOVALUE) { else_from = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } if(is_control_type(type)) return GEN_NOVALUE; // post LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { if(value != GEN_NOVALUE) LLVMAddIncoming(phi, &value, &body_from, 1); if(else_value != GEN_NOVALUE) LLVMAddIncoming(phi, &else_value, &else_from, 1); return phi; } return GEN_NOTNEEDED; }
// Write the given type to the current type file static void doc_type(docgen_t* docgen, ast_t* type) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(type != NULL); switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, package, id, tparams, cap, ephemeral); // Find type we reference so we can link to it ast_t* target = (ast_t*)ast_data(type); assert(target != NULL); size_t link_len; char* tqfn = write_tqfn(target, NULL, &link_len); // Links are of the form: [text](target) fprintf(docgen->type_file, "[%s](%s)", ast_name(id), tqfn); pool_free_size(link_len, tqfn); doc_type_list(docgen, tparams, "\\[", ", ", "\\]"); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_UNIONTYPE: doc_type_list(docgen, type, "(", " | ", ")"); break; case TK_ISECTTYPE: doc_type_list(docgen, type, "(", " & ", ")"); break; case TK_TUPLETYPE: doc_type_list(docgen, type, "(", " , ", ")"); break; case TK_TYPEPARAMREF: { AST_GET_CHILDREN(type, id, cap, ephemeral); fprintf(docgen->type_file, "%s", ast_name(id)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); doc_type(docgen, left); fprintf(docgen->type_file, "->"); doc_type(docgen, right); break; } case TK_THISTYPE: fprintf(docgen->type_file, "this"); break; case TK_BOXTYPE: fprintf(docgen->type_file, "box"); break; default: assert(0); } }
ast_t* viewpoint_reifytypeparam(ast_t* type, ast_t* typeparamref) { pony_assert(ast_id(typeparamref) == TK_TYPEPARAMREF); AST_GET_CHILDREN(typeparamref, id, cap, eph); switch(ast_id(cap)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: return NULL; case TK_CAP_SEND: { ast_t* tuple = ast_from(type, TK_TUPLETYPE); replace_typeparam(tuple, type, typeparamref, TK_ISO, ast_id(eph)); replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE); return tuple; } case TK_CAP_SHARE: { ast_t* tuple = ast_from(type, TK_TUPLETYPE); replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE); return tuple; } case TK_CAP_READ: { ast_t* tuple = ast_from(type, TK_TUPLETYPE); replace_typeparam(tuple, type, typeparamref, TK_REF, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_BOX, TK_NONE); return tuple; } case TK_CAP_ALIAS: { ast_t* tuple = ast_from(type, TK_TUPLETYPE); replace_typeparam(tuple, type, typeparamref, TK_REF, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_BOX, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE); return tuple; } case TK_CAP_ANY: { ast_t* tuple = ast_from(type, TK_TUPLETYPE); replace_typeparam(tuple, type, typeparamref, TK_ISO, ast_id(eph)); replace_typeparam(tuple, type, typeparamref, TK_TRN, ast_id(eph)); replace_typeparam(tuple, type, typeparamref, TK_REF, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_BOX, TK_NONE); replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE); return tuple; } default: {} } pony_assert(0); return NULL; }