Commit 771e88951a

Andrew Kelley <andrew@ziglang.org>
2019-06-09 00:51:31
result location mechanism for struct initialization
```zig export fn entry() void { const static = Foo{ .x = 9, .bar = Bar{ .y = 10 }, }; const runtime = foo(true); } fn foo(c: bool) Foo { return Foo{ .x = 12, .bar = if (c) bar1() else bar2(), }; } fn bar1() Bar { return Bar{ .y = 34 }; } fn bar2() Bar { return Bar{ .y = 56 }; } ``` ```llvm @0 = internal unnamed_addr constant %Foo { i32 9, %Bar { i32 10 } }, align 4 @1 = internal unnamed_addr constant %Bar { i32 34 }, align 4 @2 = internal unnamed_addr constant %Bar { i32 56 }, align 4 define void @entry() #2 !dbg !35 { Entry: %runtime = alloca %Foo, align 4 call void @llvm.dbg.declare(metadata %Foo* @0, metadata !39, metadata !DIExpression()), !dbg !50 call fastcc void @foo(%Foo* sret %runtime, i1 true), !dbg !51 call void @llvm.dbg.declare(metadata %Foo* %runtime, metadata !49, metadata !DIExpression()), !dbg !52 ret void, !dbg !53 } define internal fastcc void @foo(%Foo* nonnull sret, i1) unnamed_addr #2 !dbg !54 { Entry: %c = alloca i1, align 1 store i1 %1, i1* %c, align 1 call void @llvm.dbg.declare(metadata i1* %c, metadata !60, metadata !DIExpression()), !dbg !61 %2 = getelementptr inbounds %Foo, %Foo* %0, i32 0, i32 0, !dbg !62 store i32 12, i32* %2, align 4, !dbg !62 %3 = getelementptr inbounds %Foo, %Foo* %0, i32 0, i32 1, !dbg !64 %4 = load i1, i1* %c, align 1, !dbg !65 br i1 %4, label %Then, label %Else, !dbg !65 Then: ; preds = %Entry call fastcc void @bar1(%Bar* sret %3), !dbg !66 br label %EndIf, !dbg !64 Else: ; preds = %Entry call fastcc void @bar2(%Bar* sret %3), !dbg !67 br label %EndIf, !dbg !64 EndIf: ; preds = %Else, %Then ret void, !dbg !68 } define internal fastcc void @bar1(%Bar* nonnull sret) unnamed_addr #2 !dbg !69 { Entry: %1 = bitcast %Bar* %0 to i8*, !dbg !73 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%Bar* @1 to i8*), i64 4, i1 false), !dbg !73 ret void, !dbg !73 } define internal fastcc void @bar2(%Bar* nonnull sret) unnamed_addr #2 !dbg !75 { Entry: %1 = bitcast %Bar* %0 to i8*, !dbg !76 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%Bar* @2 to i8*), i64 4, i1 false), !dbg !76 ret void, !dbg !76 } !39 = !DILocalVariable(name: "static", scope: !40, file: !5, line: 2, type: !41) !49 = !DILocalVariable(name: "runtime", scope: !40, file: !5, line: 6, type: !41) ```
1 parent 52eb347
src/all_types.hpp
@@ -1957,6 +1957,7 @@ enum ScopeId {
     ScopeIdCompTime,
     ScopeIdCoroPrelude,
     ScopeIdRuntime,
+    ScopeIdElide,
 };
 
 struct Scope {
@@ -1970,6 +1971,14 @@ struct Scope {
     ScopeId id;
 };
 
+// This scope, when activated, causes all the instructions in the scope to be omitted
+// from the generated code.
+struct ScopeElide {
+    Scope base;
+
+    bool activated;
+};
+
 // This scope comes from global declarations or from
 // declarations in a container declaration
 // NodeTypeContainerDecl
@@ -2189,7 +2198,6 @@ enum IrInstructionId {
     IrInstructionIdResizeSlice,
     IrInstructionIdContainerInitList,
     IrInstructionIdContainerInitFields,
-    IrInstructionIdStructInit,
     IrInstructionIdUnionInit,
     IrInstructionIdUnreachable,
     IrInstructionIdTypeOf,
@@ -2279,6 +2287,7 @@ enum IrInstructionId {
     IrInstructionIdPtrType,
     IrInstructionIdAlignCast,
     IrInstructionIdImplicitCast,
+    IrInstructionIdResolveResult,
     IrInstructionIdOpaqueType,
     IrInstructionIdSetAlignStack,
     IrInstructionIdArgType,
@@ -2366,6 +2375,7 @@ struct IrInstructionCondBr {
     IrBasicBlock *then_block;
     IrBasicBlock *else_block;
     IrInstruction *is_comptime;
+    ResultLoc *result_loc;
 };
 
 struct IrInstructionBr {
@@ -2646,20 +2656,6 @@ struct IrInstructionContainerInitFields {
     IrInstructionContainerInitFieldsField *fields;
 };
 
-struct IrInstructionStructInitField {
-    IrInstruction *value;
-    TypeStructField *type_struct_field;
-};
-
-struct IrInstructionStructInit {
-    IrInstruction base;
-
-    ZigType *struct_type;
-    size_t field_count;
-    IrInstructionStructInitField *fields;
-    LLVMValueRef tmp_ptr;
-};
-
 struct IrInstructionUnionInit {
     IrInstruction base;
 
@@ -3581,13 +3577,22 @@ struct IrInstructionImplicitCast {
     IrInstruction *target;
 };
 
+struct IrInstructionResolveResult {
+    IrInstruction base;
+
+    ResultLoc *result_loc;
+    IrInstruction *ty;
+};
+
 enum ResultLocId {
     ResultLocIdInvalid,
     ResultLocIdNone,
     ResultLocIdVar,
+    ResultLocIdField,
     ResultLocIdReturn,
     ResultLocIdPeer,
     ResultLocIdPeerParent,
+    ResultLocIdInstruction,
 };
 
 struct ResultLoc {
@@ -3597,6 +3602,7 @@ struct ResultLoc {
     IrInstruction *source_instruction;
     IrInstruction *gen_instruction; // value to store to the result loc
     ZigType *implicit_elem_type;
+    ScopeElide *scope_elide;
 };
 
 struct ResultLocNone {
@@ -3609,6 +3615,14 @@ struct ResultLocVar {
     ZigVar *var;
 };
 
+struct ResultLocField {
+    ResultLoc base;
+
+    ResultLoc *parent;
+    Buf *name;
+    IrInstruction *container_type;
+};
+
 struct ResultLocReturn {
     ResultLoc base;
 };
@@ -3636,6 +3650,11 @@ struct ResultLocPeer {
     IrSuspendPosition suspend_pos;
 };
 
+// The result location is the source instruction
+struct ResultLocInstruction {
+    ResultLoc base;
+};
+
 static const size_t slice_ptr_index = 0;
 static const size_t slice_len_index = 1;
 
src/analyze.cpp
@@ -166,6 +166,12 @@ Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruct
     return &scope->base;
 }
 
+ScopeElide *create_elide_scope(CodeGen *g, AstNode *node, Scope *parent) {
+    ScopeElide *scope = allocate<ScopeElide>(1);
+    init_scope(g, &scope->base, ScopeIdElide, node, parent);
+    return scope;
+}
+
 ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeSuspend);
     ScopeSuspend *scope = allocate<ScopeSuspend>(1);
src/analyze.hpp
@@ -121,6 +121,7 @@ ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *
 Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
 Scope *create_coro_prelude_scope(CodeGen *g, AstNode *node, Scope *parent);
 Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime);
+ScopeElide *create_elide_scope(CodeGen *g, AstNode *node, Scope *parent);
 
 void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
 ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);
src/codegen.cpp
@@ -715,6 +715,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
         case ScopeIdCompTime:
         case ScopeIdCoroPrelude:
         case ScopeIdRuntime:
+        case ScopeIdElide:
             return get_di_scope(g, scope->parent);
     }
     zig_unreachable();
@@ -2383,7 +2384,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
 }
 
 static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
-    LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
     ZigType *return_type = return_instruction->value->value.type;
 
     if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) {
@@ -2391,13 +2391,16 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
         if (return_instruction->value->value.special != ConstValSpecialRuntime) {
             // if it's comptime we have to do this but if it's runtime trust that
             // result location mechanism took care of it.
+            LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
             gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
         }
         LLVMBuildRetVoid(g->builder);
     } else if (handle_is_ptr(return_type)) {
+        LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
         LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, "");
         LLVMBuildRet(g->builder, by_val_value);
     } else {
+        LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
         LLVMBuildRet(g->builder, value);
     }
     return nullptr;
@@ -5032,29 +5035,6 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir
     return get_handle_value(g, tag_field_ptr, tag_type, ptr_type);
 }
 
-static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, IrInstructionStructInit *instruction) {
-    for (size_t i = 0; i < instruction->field_count; i += 1) {
-        IrInstructionStructInitField *field = &instruction->fields[i];
-        TypeStructField *type_struct_field = field->type_struct_field;
-        if (!type_has_bits(type_struct_field->type_entry))
-            continue;
-
-        LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
-                (unsigned)type_struct_field->gen_index, "");
-        LLVMValueRef value = ir_llvm_value(g, field->value);
-
-        uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry);
-        uint32_t host_int_bytes = get_host_int_bytes(g, instruction->struct_type, type_struct_field);
-
-        ZigType *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry,
-                false, false, PtrLenSingle, field_align_bytes,
-                (uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes, false);
-
-        gen_assign_raw(g, field_ptr, ptr_type, value);
-    }
-    return instruction->tmp_ptr;
-}
-
 static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) {
     TypeUnionField *type_union_field = instruction->field;
 
@@ -5531,10 +5511,6 @@ static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
 }
 
 static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
-    if (!g->strip_debug_symbols) {
-        set_debug_location(g, instruction);
-    }
-
     switch (instruction->id) {
         case IrInstructionIdInvalid:
         case IrInstructionIdConst:
@@ -5609,6 +5585,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdEndExpr:
         case IrInstructionIdAllocaGen:
         case IrInstructionIdImplicitCast:
+        case IrInstructionIdResolveResult:
             zig_unreachable();
 
         case IrInstructionIdDeclVarGen:
@@ -5705,8 +5682,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction);
         case IrInstructionIdUnionTag:
             return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction);
-        case IrInstructionIdStructInit:
-            return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
         case IrInstructionIdUnionInit:
             return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction);
         case IrInstructionIdPtrCastGen:
@@ -5791,6 +5766,34 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
     zig_unreachable();
 }
 
+static bool scope_is_elided(Scope *scope) {
+    for (;;) {
+        switch (scope->id) {
+            case ScopeIdDecls:
+            case ScopeIdCompTime:
+            case ScopeIdCImport:
+                zig_unreachable();
+            case ScopeIdElide:
+                if (reinterpret_cast<ScopeElide *>(scope)->activated)
+                    return true;
+                // fallthrough
+            case ScopeIdBlock:
+            case ScopeIdDefer:
+            case ScopeIdDeferExpr:
+            case ScopeIdVarDecl:
+            case ScopeIdLoop:
+            case ScopeIdSuspend:
+            case ScopeIdCoroPrelude:
+            case ScopeIdRuntime:
+                scope = scope->parent;
+                continue;
+            case ScopeIdFnDef:
+                return false;
+        }
+        zig_unreachable();
+    }
+}
+
 static void ir_render(CodeGen *g, ZigFn *fn_entry) {
     assert(fn_entry);
 
@@ -5806,7 +5809,12 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
             if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))
                 continue;
 
-            instruction->llvm_value = ir_render_instruction(g, executable, instruction);
+            if (!scope_is_elided(instruction->scope)) {
+                if (!g->strip_debug_symbols) {
+                    set_debug_location(g, instruction);
+                }
+                instruction->llvm_value = ir_render_instruction(g, executable, instruction);
+            }
         }
         current_block->llvm_exit_block = LLVMGetInsertBlock(g->builder);
     }
@@ -6891,9 +6899,6 @@ static void do_code_gen(CodeGen *g) {
             } else if (instruction->id == IrInstructionIdContainerInitList) {
                 IrInstructionContainerInitList *container_init_list_instruction = (IrInstructionContainerInitList *)instruction;
                 slot = &container_init_list_instruction->tmp_ptr;
-            } else if (instruction->id == IrInstructionIdStructInit) {
-                IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction;
-                slot = &struct_init_instruction->tmp_ptr;
             } else if (instruction->id == IrInstructionIdUnionInit) {
                 IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction;
                 slot = &union_init_instruction->tmp_ptr;
src/ir.cpp
@@ -616,10 +616,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) {
     return IrInstructionIdRef;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
-    return IrInstructionIdStructInit;
-}
-
 static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) {
     return IrInstructionIdUnionInit;
 }
@@ -888,6 +884,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionImplicitCast *)
     return IrInstructionIdImplicitCast;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionResolveResult *) {
+    return IrInstructionIdResolveResult;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionOpaqueType *) {
     return IrInstructionIdOpaqueType;
 }
@@ -1517,20 +1517,6 @@ static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scop
     return &container_init_fields_instruction->base;
 }
 
-static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
-        ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields)
-{
-    IrInstructionStructInit *struct_init_instruction = ir_build_instruction<IrInstructionStructInit>(irb, scope, source_node);
-    struct_init_instruction->struct_type = struct_type;
-    struct_init_instruction->field_count = field_count;
-    struct_init_instruction->fields = fields;
-
-    for (size_t i = 0; i < field_count; i += 1)
-        ir_ref_instruction(fields[i].value, irb->current_basic_block);
-
-    return &struct_init_instruction->base;
-}
-
 static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
         ZigType *union_type, TypeUnionField *field, IrInstruction *init_value)
 {
@@ -2764,6 +2750,18 @@ static IrInstruction *ir_build_implicit_cast(IrBuilder *irb, Scope *scope, AstNo
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_resolve_result(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        ResultLoc *result_loc, IrInstruction *ty)
+{
+    IrInstructionResolveResult *instruction = ir_build_instruction<IrInstructionResolveResult>(irb, scope, source_node);
+    instruction->result_loc = result_loc;
+    instruction->ty = ty;
+
+    ir_ref_instruction(ty, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_opaque_type(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     IrInstructionOpaqueType *instruction = ir_build_instruction<IrInstructionOpaqueType>(irb, scope, source_node);
 
@@ -3220,6 +3218,7 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
             case ScopeIdSuspend:
             case ScopeIdCompTime:
             case ScopeIdRuntime:
+            case ScopeIdElide:
                 scope = scope->parent;
                 continue;
             case ScopeIdDeferExpr:
@@ -3276,6 +3275,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
             case ScopeIdSuspend:
             case ScopeIdCompTime:
             case ScopeIdRuntime:
+            case ScopeIdElide:
                 scope = scope->parent;
                 continue;
             case ScopeIdDeferExpr:
@@ -5549,7 +5549,9 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
     zig_unreachable();
 }
 
-static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
+static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
     assert(node->type == NodeTypeContainerInitExpr);
 
     AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
@@ -5559,39 +5561,61 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
     if (container_type == irb->codegen->invalid_instruction)
         return container_type;
 
-    if (kind == ContainerInitKindStruct) {
-        size_t field_count = container_init_expr->entries.length;
-        IrInstructionContainerInitFieldsField *fields = allocate<IrInstructionContainerInitFieldsField>(field_count);
-        for (size_t i = 0; i < field_count; i += 1) {
-            AstNode *entry_node = container_init_expr->entries.at(i);
-            assert(entry_node->type == NodeTypeStructValueField);
-
-            Buf *name = entry_node->data.struct_val_field.name;
-            AstNode *expr_node = entry_node->data.struct_val_field.expr;
-            IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope);
-            if (expr_value == irb->codegen->invalid_instruction)
-                return expr_value;
-
-            fields[i].name = name;
-            fields[i].value = expr_value;
-            fields[i].source_node = entry_node;
-        }
-        return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields);
-    } else if (kind == ContainerInitKindArray) {
-        size_t item_count = container_init_expr->entries.length;
-        IrInstruction **values = allocate<IrInstruction *>(item_count);
-        for (size_t i = 0; i < item_count; i += 1) {
-            AstNode *expr_node = container_init_expr->entries.at(i);
-            IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope);
-            if (expr_value == irb->codegen->invalid_instruction)
-                return expr_value;
-
-            values[i] = expr_value;
-        }
-        return ir_build_container_init_list(irb, scope, node, container_type, item_count, values);
-    } else {
-        zig_unreachable();
+    switch (kind) {
+        case ContainerInitKindStruct: {
+            src_assert(result_loc->scope_elide == nullptr, node);
+            result_loc->scope_elide = create_elide_scope(irb->codegen, node, scope);
+            size_t field_count = container_init_expr->entries.length;
+            IrInstructionContainerInitFieldsField *fields = allocate<IrInstructionContainerInitFieldsField>(field_count);
+            for (size_t i = 0; i < field_count; i += 1) {
+                AstNode *entry_node = container_init_expr->entries.at(i);
+                assert(entry_node->type == NodeTypeStructValueField);
+
+                Buf *name = entry_node->data.struct_val_field.name;
+                AstNode *expr_node = entry_node->data.struct_val_field.expr;
+
+                ResultLoc *child_result_loc = nullptr;
+                if (result_loc != nullptr) {
+                    IrInstruction *container_ptr = ir_build_resolve_result(irb, &result_loc->scope_elide->base,
+                            expr_node, result_loc, container_type);
+                    IrInstruction *field_ptr = ir_build_field_ptr(irb, &result_loc->scope_elide->base, expr_node,
+                            container_ptr, name);
+                    ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
+                    result_loc_inst->base.id = ResultLocIdInstruction;
+                    result_loc_inst->base.source_instruction = field_ptr;
+                    ir_ref_instruction(field_ptr, irb->current_basic_block);
+                    child_result_loc = &result_loc_inst->base;
+                }
+
+                IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, &result_loc->scope_elide->base,
+                        LValNone, child_result_loc);
+                if (expr_value == irb->codegen->invalid_instruction)
+                    return expr_value;
+
+                fields[i].name = name;
+                fields[i].value = expr_value;
+                fields[i].source_node = entry_node;
+            }
+            IrInstruction *init_fields = ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields);
+
+            return ir_lval_wrap(irb, scope, init_fields, lval, result_loc);
+        }
+        case ContainerInitKindArray: {
+            size_t item_count = container_init_expr->entries.length;
+            IrInstruction **values = allocate<IrInstruction *>(item_count);
+            for (size_t i = 0; i < item_count; i += 1) {
+                AstNode *expr_node = container_init_expr->entries.at(i);
+                IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope);
+                if (expr_value == irb->codegen->invalid_instruction)
+                    return expr_value;
+
+                values[i] = expr_value;
+            }
+            IrInstruction *init_list = ir_build_container_init_list(irb, scope, node, container_type, item_count, values);
+            return ir_lval_wrap(irb, scope, init_list, lval, result_loc);
+        }
     }
+    zig_unreachable();
 }
 
 static ResultLocVar *create_var_result_loc(IrInstruction *alloca, ZigVar *var) {
@@ -7885,7 +7909,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
         case NodeTypePrefixOpExpr:
             return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc);
         case NodeTypeContainerInitExpr:
-            return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval, result_loc);
+            return ir_gen_container_init_expr(irb, scope, node, lval, result_loc);
         case NodeTypeVariableDeclaration:
             return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval, result_loc);
         case NodeTypeWhileExpr:
@@ -14468,7 +14492,9 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in
     return &result->base;
 }
 
-static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, ResultLoc *result_loc) {
+static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInstruction *suspend_source_instr,
+        ResultLoc *result_loc)
+{
     switch (result_loc->id) {
         case ResultLocIdInvalid:
         case ResultLocIdPeerParent:
@@ -14476,6 +14502,30 @@ static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, ResultLoc *result_lo
         case ResultLocIdNone:
         case ResultLocIdVar:
             return nullptr;
+        case ResultLocIdInstruction:
+            return result_loc->source_instruction->child->value.type;
+        case ResultLocIdField: {
+            if (result_loc->resolved_loc != nullptr) {
+                ZigType *ptr_type = result_loc->resolved_loc->value.type;
+                assert(ptr_type->id == ZigTypeIdPointer);
+                return ptr_type->data.pointer.child_type;
+            }
+            ResultLocField *result_loc_field = reinterpret_cast<ResultLocField *>(result_loc);
+            ZigType *container_type = ir_resolve_type(ira, result_loc_field->container_type->child);
+            if (type_is_invalid(container_type))
+                return ira->codegen->builtin_types.entry_invalid;
+            if (container_type->id == ZigTypeIdStruct) {
+                TypeStructField *field = find_struct_type_field(container_type, result_loc_field->name);
+                if (field == nullptr) {
+                    return ira->codegen->builtin_types.entry_invalid;
+                }
+                return field->type_entry;
+            } else if (container_type->id == ZigTypeIdUnion) {
+                zig_panic("TODO");
+            } else {
+                zig_unreachable();
+            }
+        }
         case ResultLocIdReturn:
             return ira->explicit_return_type;
         case ResultLocIdPeer:
@@ -14491,7 +14541,10 @@ static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_s
         ResultLoc *result_loc, ZigType *value_type, IrInstruction *value)
 {
     if (result_loc->resolved_loc != nullptr) {
-        return result_loc->resolved_loc;
+        // allow to redo the result location if the value is known and comptime and the previous one isn't
+        if (value == nullptr || !instr_is_comptime(value) || instr_is_comptime(result_loc->resolved_loc)) {
+            return result_loc->resolved_loc;
+        }
     }
     result_loc->gen_instruction = value;
     result_loc->implicit_elem_type = value_type;
@@ -14524,7 +14577,7 @@ static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_s
                 return ira->codegen->invalid_instruction;
             bool is_comptime = force_comptime || (value != nullptr &&
                     value->value.special != ConstValSpecialRuntime && result_loc_var->var->gen_is_const);
-            if (alloca_src->base.child == nullptr) {
+            if (alloca_src->base.child == nullptr || is_comptime) {
                 uint32_t align = 0;
                 if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, &align)) {
                     return ira->codegen->invalid_instruction;
@@ -14539,12 +14592,40 @@ static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_s
                     alloca_gen = ir_analyze_alloca(ira, result_loc->source_instruction, value_type, align,
                             alloca_src->name_hint, force_comptime);
                 }
+                if (alloca_src->base.child != nullptr) {
+                    alloca_src->base.child->ref_count = 0;
+                }
                 alloca_src->base.child = alloca_gen;
             }
             result_loc->written = true;
             result_loc->resolved_loc = is_comptime ? nullptr : alloca_src->base.child;
             return result_loc->resolved_loc;
         }
+        case ResultLocIdInstruction: {
+            result_loc->written = true;
+            result_loc->resolved_loc = result_loc->source_instruction->child;
+            return result_loc->resolved_loc;
+        }
+        case ResultLocIdField: {
+            ResultLocField *result_loc_field = reinterpret_cast<ResultLocField *>(result_loc);
+
+            ZigType *container_type = ir_resolve_type(ira, result_loc_field->container_type->child);
+            if (type_is_invalid(container_type))
+                return ira->codegen->invalid_instruction;
+
+            IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr,
+                    result_loc_field->parent, container_type, nullptr);
+            if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) ||
+                parent_result_loc->value.type->id == ZigTypeIdUnreachable)
+            {
+                return parent_result_loc;
+            }
+
+            result_loc->written = true;
+            result_loc->resolved_loc = ir_analyze_container_field_ptr(ira, result_loc_field->name,
+                    suspend_source_instr, parent_result_loc, container_type);
+            return result_loc->resolved_loc;
+        }
         case ResultLocIdReturn: {
             bool is_comptime = value != nullptr && value->value.special != ConstValSpecialRuntime;
             if (is_comptime) return nullptr;
@@ -14593,7 +14674,7 @@ static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_s
                         ira->resume_stack.append(opposite_peer->suspend_pos);
                     }
                 }
-                ZigType *expected_type = ir_result_loc_expected_type(ira, peer_parent->parent);
+                ZigType *expected_type = ir_result_loc_expected_type(ira, suspend_source_instr, peer_parent->parent);
                 peer_parent->resolved_type = ir_resolve_peer_types(ira,
                         peer_parent->base.source_instruction->source_node, expected_type, instructions,
                         peer_parent->peer_count);
@@ -14626,6 +14707,12 @@ static IrInstruction *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrIns
     return ir_implicit_cast(ira, target, dest_type);
 }
 
+static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstructionResolveResult *instruction) {
+    ZigType *ty = ir_resolve_type(ira, instruction->ty->child);
+    if (type_is_invalid(ty))
+        return ira->codegen->invalid_instruction;
+    return ir_resolve_result(ira, &instruction->base, instruction->result_loc, ty, nullptr);
+}
 
 static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,
         ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
@@ -18330,8 +18417,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
 
     AstNode **field_assign_nodes = allocate<AstNode *>(actual_field_count);
 
-    IrInstructionStructInitField *new_fields = allocate<IrInstructionStructInitField>(actual_field_count);
-
     bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
         || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
 
@@ -18371,9 +18456,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
         }
         field_assign_nodes[field_index] = field->source_node;
 
-        new_fields[field_index].value = casted_field_value;
-        new_fields[field_index].type_struct_field = type_field;
-
         if (const_val.special == ConstValSpecialStatic) {
             if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
                 ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
@@ -18416,9 +18498,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
         IrInstruction *runtime_inst = ir_const(ira, instruction, field->init_val->type);
         copy_const_val(&runtime_inst->value, field->init_val, true);
 
-        new_fields[i].value = runtime_inst;
-        new_fields[i].type_struct_field = field;
-
         if (const_val.special == ConstValSpecialStatic) {
             copy_const_val(&const_val.data.x_struct.fields[i], field->init_val, true);
         }
@@ -18451,12 +18530,11 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
         return ira->codegen->invalid_instruction;
     }
 
-    IrInstruction *new_instruction = ir_build_struct_init(&ira->new_irb,
-        instruction->scope, instruction->source_node,
-        container_type, actual_field_count, new_fields);
-    new_instruction->value.type = container_type;
-    ir_add_alloca(ira, new_instruction, container_type);
-    return new_instruction;
+    // this instruction should not get to codegen
+    IrInstruction *result = ir_const(ira, instruction, container_type);
+    // this is how we signal to EndExpr the value is not comptime known
+    result->value.special = ConstValSpecialRuntime;
+    return result;
 }
 
 static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
@@ -23794,7 +23872,18 @@ static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstruct
     if (type_is_invalid(value->value.type))
         return ira->codegen->invalid_instruction;
 
-    if (!instruction->result_loc->written) {
+    bool want_resolve_result = instruction->result_loc->written;
+    if (instruction->result_loc->written) {
+        if (instruction->result_loc->scope_elide != nullptr && instr_is_comptime(value)) {
+            want_resolve_result = true;
+            instruction->result_loc->scope_elide->activated = true;
+        } else {
+            want_resolve_result = false;
+        }
+    } else {
+        want_resolve_result = true;
+    }
+    if (want_resolve_result) {
         IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
                 value->value.type, value);
         if (result_loc != nullptr) {
@@ -23815,7 +23904,6 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
     switch (instruction->id) {
         case IrInstructionIdInvalid:
         case IrInstructionIdWidenOrShorten:
-        case IrInstructionIdStructInit:
         case IrInstructionIdUnionInit:
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdUnionFieldPtr:
@@ -24036,6 +24124,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
             return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction);
         case IrInstructionIdImplicitCast:
             return ir_analyze_instruction_implicit_cast(ira, (IrInstructionImplicitCast *)instruction);
+        case IrInstructionIdResolveResult:
+            return ir_analyze_instruction_resolve_result(ira, (IrInstructionResolveResult *)instruction);
         case IrInstructionIdOpaqueType:
             return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction);
         case IrInstructionIdSetAlignStack:
@@ -24260,7 +24350,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
-        case IrInstructionIdStructInit:
         case IrInstructionIdUnionInit:
         case IrInstructionIdFieldPtr:
         case IrInstructionIdElemPtr:
@@ -24326,6 +24415,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdTypeId:
         case IrInstructionIdAlignCast:
         case IrInstructionIdImplicitCast:
+        case IrInstructionIdResolveResult:
         case IrInstructionIdOpaqueType:
         case IrInstructionIdArgType:
         case IrInstructionIdTagType:
src/ir_print.cpp
@@ -207,6 +207,18 @@ static void ir_print_result_loc_var(IrPrint *irp, ResultLocVar *result_loc_var)
     fprintf(irp->f, ")");
 }
 
+static void ir_print_result_loc_instruction(IrPrint *irp, ResultLocInstruction *result_loc_inst) {
+    fprintf(irp->f, "inst(");
+    ir_print_other_instruction(irp, result_loc_inst->base.source_instruction);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_result_loc_field(IrPrint *irp, ResultLocField *result_loc_field) {
+    fprintf(irp->f, "field(name=%s,type=", buf_ptr(result_loc_field->name));
+    ir_print_other_instruction(irp, result_loc_field->container_type);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_result_loc_peer(IrPrint *irp, ResultLocPeer *result_loc_peer) {
     fprintf(irp->f, "peer(next=");
     ir_print_other_block(irp, result_loc_peer->next_bb);
@@ -225,6 +237,10 @@ static void ir_print_result_loc(IrPrint *irp, ResultLoc *result_loc) {
             return;
         case ResultLocIdVar:
             return ir_print_result_loc_var(irp, (ResultLocVar *)result_loc);
+        case ResultLocIdInstruction:
+            return ir_print_result_loc_instruction(irp, (ResultLocInstruction *)result_loc);
+        case ResultLocIdField:
+            return ir_print_result_loc_field(irp, (ResultLocField *)result_loc);
         case ResultLocIdPeer:
             return ir_print_result_loc_peer(irp, (ResultLocPeer *)result_loc);
         case ResultLocIdPeerParent:
@@ -352,18 +368,6 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI
     fprintf(irp->f, "} // container init");
 }
 
-static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruction) {
-    fprintf(irp->f, "%s {", buf_ptr(&instruction->struct_type->name));
-    for (size_t i = 0; i < instruction->field_count; i += 1) {
-        IrInstructionStructInitField *field = &instruction->fields[i];
-        Buf *field_name = field->type_struct_field->name;
-        const char *comma = (i == 0) ? "" : ", ";
-        fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field_name));
-        ir_print_other_instruction(irp, field->value);
-    }
-    fprintf(irp->f, "} // struct init");
-}
-
 static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) {
     Buf *field_name = instruction->field->enum_field->name;
 
@@ -1265,6 +1269,12 @@ static void ir_print_implicit_cast(IrPrint *irp, IrInstructionImplicitCast *inst
     fprintf(irp->f, ")");
 }
 
+static void ir_print_resolve_result(IrPrint *irp, IrInstructionResolveResult *instruction) {
+    fprintf(irp->f, "ResolveResult(");
+    ir_print_result_loc(irp, instruction->result_loc);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_opaque_type(IrPrint *irp, IrInstructionOpaqueType *instruction) {
     fprintf(irp->f, "@OpaqueType()");
 }
@@ -1588,9 +1598,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdContainerInitFields:
             ir_print_container_init_fields(irp, (IrInstructionContainerInitFields *)instruction);
             break;
-        case IrInstructionIdStructInit:
-            ir_print_struct_init(irp, (IrInstructionStructInit *)instruction);
-            break;
         case IrInstructionIdUnionInit:
             ir_print_union_init(irp, (IrInstructionUnionInit *)instruction);
             break;
@@ -1900,6 +1907,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdImplicitCast:
             ir_print_implicit_cast(irp, (IrInstructionImplicitCast *)instruction);
             break;
+        case IrInstructionIdResolveResult:
+            ir_print_resolve_result(irp, (IrInstructionResolveResult *)instruction);
+            break;
         case IrInstructionIdOpaqueType:
             ir_print_opaque_type(irp, (IrInstructionOpaqueType *)instruction);
             break;
BRANCH_TODO
@@ -1,8 +1,8 @@
 Scratch pad for stuff to do before merging master
 =================================================
 
- * struct initializations
- * function call parameters
+ * array initializations
+ * union initializations
  * bitCast
 
 look at all the ir_gen_node ir_gen_node_extra calls and make sure result locations are properly propagated
@@ -25,7 +25,3 @@ inferred comptime
     return ir_build_ref(irb, scope, value->source_node, value, false, false);
 
 handle if with no else
-
-
-
-