static ast_t* find_embed_constructor_receiver(ast_t* call) { call_tuple_indices_t tuple_indices = {NULL, 0, 4}; tuple_indices_init(&tuple_indices); ast_t* parent = make_tuple_indices(&tuple_indices, call); ast_t* fieldref = NULL; if((parent != NULL) && (ast_id(parent) == TK_ASSIGN)) { // Traverse the LHS of the assignment looking for what our constructor call // is assigned to. ast_t* current = ast_child(parent); 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; size_t index = tuple_indices_pop(&tuple_indices); current = ast_childidx(parent, index); } else { current = ast_childlast(parent); } } if(ast_id(current) == TK_EMBEDREF) fieldref = current; } tuple_indices_destroy(&tuple_indices); return fieldref; }
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; }