Commit b025193de5

Andrew Kelley <andrew@ziglang.org>
2019-06-17 19:31:19
inferred comptime values rather than elided scopes
because of this example: ```zig export fn entry(b: bool) usize { var runtime = [1]i32{3}; comptime var i: usize = 0; inline while (i < 2) : (i += 1) { const result = if (i == 0) [1]i32{2} else runtime; } comptime { return i; } } ``` The problem is that the concept of "resetting" a result location, introduced in the previous commit, cannot handle elision scopes. This concept is inherently broken with inline loops.
1 parent 9564c05
src/all_types.hpp
@@ -203,6 +203,9 @@ enum ConstPtrMut {
     // The pointer points to memory that is known only at runtime.
     // For example it may point to the initializer value of a variable.
     ConstPtrMutRuntimeVar,
+    // The pointer points to memory for which it must be inferred whether the
+    // value is comptime known or not.
+    ConstPtrMutInfer,
 };
 
 struct ConstPtrValue {
@@ -1957,7 +1960,6 @@ enum ScopeId {
     ScopeIdCompTime,
     ScopeIdCoroPrelude,
     ScopeIdRuntime,
-    ScopeIdElide,
 };
 
 struct Scope {
@@ -1971,14 +1973,6 @@ 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
@@ -2655,6 +2649,7 @@ struct IrInstructionContainerInitFieldsField {
     IrInstruction *value;
     AstNode *source_node;
     TypeStructField *type_struct_field;
+    IrInstruction *result_loc;
 };
 
 struct IrInstructionContainerInitFields {
@@ -3655,7 +3650,6 @@ struct ResultLoc {
     IrInstruction *source_instruction;
     IrInstruction *gen_instruction; // value to store to the result loc
     ZigType *implicit_elem_type;
-    ScopeElide *scope_elide;
 };
 
 struct ResultLocNone {
src/analyze.cpp
@@ -166,12 +166,6 @@ 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);
@@ -4187,6 +4181,7 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
         case ConstPtrMutComptimeConst:
             hash_val += (uint32_t)4214318515;
             break;
+        case ConstPtrMutInfer:
         case ConstPtrMutComptimeVar:
             hash_val += (uint32_t)1103195694;
             break;
@@ -7286,31 +7281,3 @@ void src_assert(bool ok, AstNode *source_node) {
     const char *msg = "assertion failed. This is a bug in the Zig compiler.";
     stage2_panic(msg, strlen(msg));
 }
-
-bool scope_is_elided(Scope *scope) {
-    for (;;) {
-        switch (scope->id) {
-            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:
-            case ScopeIdCompTime:
-            case ScopeIdDecls:
-            case ScopeIdCImport:
-                return false;
-        }
-        zig_unreachable();
-    }
-}
-
src/analyze.hpp
@@ -121,7 +121,6 @@ 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);
@@ -254,6 +253,5 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
 void src_assert(bool ok, AstNode *source_node);
 bool is_container(ZigType *type_entry);
 ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name);
-bool scope_is_elided(Scope *scope);
 
 #endif
src/codegen.cpp
@@ -722,7 +722,6 @@ 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();
@@ -5761,12 +5760,10 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
             if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))
                 continue;
 
-            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);
+            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);
     }
src/ir.cpp
@@ -196,6 +196,8 @@ static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruct
         IrInstruction *base_ptr, bool safety_check_on, bool initializing);
 static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr,
         IrInstruction *base_ptr, bool initializing);
+static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
+        IrInstruction *ptr, IrInstruction *uncasted_value);
 
 static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
     assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -3363,7 +3365,6 @@ 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:
@@ -3420,7 +3421,6 @@ 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:
@@ -5758,15 +5758,8 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
                 return irb->codegen->invalid_instruction;
             }
 
-            IrInstruction *container_ptr = nullptr;
-            if (!ir_should_inline(irb->exec, scope)) {
-                src_assert(parent_result_loc->scope_elide == nullptr, node);
-                parent_result_loc->scope_elide = create_elide_scope(irb->codegen, node, scope);
-
-                src_assert(parent_result_loc != nullptr, node);
-                container_ptr = ir_build_resolve_result(irb, &parent_result_loc->scope_elide->base,
-                        node, parent_result_loc, container_type);
-            }
+            IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc,
+                    container_type);
 
             size_t field_count = container_init_expr->entries.length;
             IrInstructionContainerInitFieldsField *fields = allocate<IrInstructionContainerInitFieldsField>(field_count);
@@ -5777,27 +5770,22 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
                 Buf *name = entry_node->data.struct_val_field.name;
                 AstNode *expr_node = entry_node->data.struct_val_field.expr;
 
-                Scope *val_scope = scope;
-                ResultLoc *child_result_loc = nullptr;
-                if (container_ptr != nullptr) {
-                    IrInstruction *field_ptr = ir_build_field_ptr(irb, &parent_result_loc->scope_elide->base,
-                            expr_node, container_ptr, name, true);
-                    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;
-                    val_scope = &parent_result_loc->scope_elide->base;
-                }
+                IrInstruction *field_ptr = ir_build_field_ptr(irb, scope, expr_node, container_ptr, name, true);
+                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);
+                ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
 
-                IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, val_scope, LValNone,
-                        child_result_loc);
+                IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
+                        &result_loc_inst->base);
                 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;
+                fields[i].result_loc = field_ptr;
             }
             IrInstruction *init_fields = ir_build_container_init_fields(irb, scope, node, container_type,
                     field_count, fields, container_ptr);
@@ -5812,36 +5800,23 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
                 container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type);
             }
 
-            IrInstruction *container_ptr = nullptr;
-            if (!ir_should_inline(irb->exec, scope)) {
-                src_assert(parent_result_loc->scope_elide == nullptr, node);
-                parent_result_loc->scope_elide = create_elide_scope(irb->codegen, node, scope);
-
-                container_ptr = ir_build_resolve_result(irb, &parent_result_loc->scope_elide->base,
-                        node, parent_result_loc, container_type);
-            }
+            IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc,
+                    container_type);
 
             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);
 
-                ResultLoc *child_result_loc = nullptr;
-                Scope *val_scope = scope;
-                if (container_ptr != nullptr) {
-                    IrInstruction *elem_index = ir_build_const_usize(irb, &parent_result_loc->scope_elide->base,
-                            expr_node, i);
-                    IrInstruction *elem_ptr = ir_build_elem_ptr(irb, &parent_result_loc->scope_elide->base,
-                            expr_node, container_ptr, elem_index, false, PtrLenSingle, true);
-                    ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
-                    result_loc_inst->base.id = ResultLocIdInstruction;
-                    result_loc_inst->base.source_instruction = elem_ptr;
-                    ir_ref_instruction(elem_ptr, irb->current_basic_block);
-                    child_result_loc = &result_loc_inst->base;
-                    val_scope = &parent_result_loc->scope_elide->base;
-                }
+                IrInstruction *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
+                IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, elem_index,
+                        false, PtrLenSingle, true);
+                ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
+                result_loc_inst->base.id = ResultLocIdInstruction;
+                result_loc_inst->base.source_instruction = elem_ptr;
+                ir_ref_instruction(elem_ptr, irb->current_basic_block);
 
-                IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, val_scope, LValNone,
-                        child_result_loc);
+                IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
+                        &result_loc_inst->base);
                 if (expr_value == irb->codegen->invalid_instruction)
                     return expr_value;
 
@@ -8651,8 +8626,6 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec
     IrBasicBlock *bb = exec->basic_block_list.at(0);
     for (size_t i = 0; i < bb->instruction_list.length; i += 1) {
         IrInstruction *instruction = bb->instruction_list.at(i);
-        if (scope_is_elided(instruction->scope))
-            continue;
         if (instruction->id == IrInstructionIdReturn) {
             IrInstructionReturn *ret_inst = (IrInstructionReturn *)instruction;
             IrInstruction *value = ret_inst->value;
@@ -12745,9 +12718,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
             ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value"));
             return ira->codegen->invalid_instruction;
         }
-        if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
-            ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
-        {
+        if (ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
             ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
             if (pointee->special != ConstValSpecialRuntime) {
                 IrInstruction *result = ir_const(ira, source_instruction, child_type);
@@ -14487,7 +14458,25 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
     }
 
     if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) {
-        if (var->mem_slot_index != SIZE_MAX) {
+        // Resolve ConstPtrMutInfer
+        if (var->gen_is_const) {
+            var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
+        } else if (is_comptime_var) {
+            var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar;
+        } else {
+            // we need a runtime ptr but we have a comptime val.
+            // since it's a comptime val there are no instructions for it.
+            // we memcpy the init value here
+            IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr, nullptr);
+            // If this assertion trips, something is wrong with the IR instructions, because
+            // we expected the above deref to return a constant value, but it created a runtime
+            // instruction.
+            assert(deref->value.special != ConstValSpecialRuntime);
+            var_ptr->value.special = ConstValSpecialRuntime;
+            ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref);
+        }
+
+        if (var_ptr->value.special == ConstValSpecialStatic && var->mem_slot_index != SIZE_MAX) {
             assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
             ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
             copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const);
@@ -14818,9 +14807,9 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in
     pointee->special = ConstValSpecialUndef;
 
     IrInstructionAllocaGen *result = ir_create_alloca_gen(ira, source_inst, align, name_hint);
-    result->base.value.special = force_comptime ? ConstValSpecialStatic : ConstValSpecialRuntime;
+    result->base.value.special = ConstValSpecialStatic;
     result->base.value.data.x_ptr.special = ConstPtrSpecialRef;
-    result->base.value.data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutRuntimeVar;
+    result->base.value.data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer;
     result->base.value.data.x_ptr.data.ref.pointee = pointee;
 
     if ((err = type_resolve(ira->codegen, var_type, ResolveStatusZeroBitsKnown)))
@@ -15109,7 +15098,10 @@ static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrIn
     ZigType *implicit_elem_type = ir_resolve_type(ira, instruction->ty->child);
     if (type_is_invalid(implicit_elem_type))
         return ira->codegen->invalid_instruction;
-    return ir_resolve_result(ira, &instruction->base, instruction->result_loc, implicit_elem_type, nullptr);
+    IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, implicit_elem_type, nullptr);
+    if (result_loc != nullptr)
+        return result_loc;
+    zig_panic("TODO");
 }
 
 static void ir_reset_result(ResultLoc *result_loc) {
@@ -15431,7 +15423,9 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
             ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
             return ira->codegen->invalid_instruction;
         }
-        if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) {
+        if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar ||
+            ptr->value.data.x_ptr.mut == ConstPtrMutInfer)
+        {
             if (instr_is_comptime(value)) {
                 ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node);
                 if (dest_val == nullptr)
@@ -15451,12 +15445,16 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
                     return ir_const_void(ira, source_instr);
                 }
             }
-            ir_add_error(ira, source_instr,
-                    buf_sprintf("cannot store runtime value in compile time variable"));
-            ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
-            dest_val->type = ira->codegen->builtin_types.entry_invalid;
+            if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) {
+                ptr->value.special = ConstValSpecialRuntime;
+            } else {
+                ir_add_error(ira, source_instr,
+                        buf_sprintf("cannot store runtime value in compile time variable"));
+                ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
+                dest_val->type = ira->codegen->builtin_types.entry_invalid;
 
-            return ira->codegen->invalid_instruction;
+                return ira->codegen->invalid_instruction;
+            }
         }
     }
 
@@ -17091,6 +17089,81 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
     return ira->codegen->invalid_instruction;
 }
 
+static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr,
+        TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing)
+{
+    switch (type_has_one_possible_value(ira->codegen, field->type_entry)) {
+        case OnePossibleValueInvalid:
+            return ira->codegen->invalid_instruction;
+        case OnePossibleValueYes: {
+            IrInstruction *elem = ir_const(ira, source_instr, field->type_entry);
+            return ir_get_ref(ira, source_instr, elem, false, false);
+        }
+        case OnePossibleValueNo:
+            break;
+    }
+    assert(struct_ptr->value.type->id == ZigTypeIdPointer);
+    bool is_packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
+    uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry);
+    uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host;
+    uint32_t ptr_host_int_bytes = struct_ptr->value.type->data.pointer.host_int_bytes;
+    uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ?
+        get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes;
+    bool is_const = struct_ptr->value.type->data.pointer.is_const;
+    bool is_volatile = struct_ptr->value.type->data.pointer.is_volatile;
+    ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
+            is_const, is_volatile, PtrLenSingle, align_bytes,
+            (uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
+            (uint32_t)host_int_bytes_for_result_type, false);
+    if (instr_is_comptime(struct_ptr)) {
+        ConstExprValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad);
+        if (!ptr_val)
+            return ira->codegen->invalid_instruction;
+
+        if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
+            ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
+            if (struct_val == nullptr)
+                return ira->codegen->invalid_instruction;
+            if (type_is_invalid(struct_val->type))
+                return ira->codegen->invalid_instruction;
+            if (struct_val->special == ConstValSpecialUndef && initializing) {
+                struct_val->data.x_struct.fields = create_const_vals(struct_type->data.structure.src_field_count);
+                struct_val->special = ConstValSpecialStatic;
+                for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) {
+                    ConstExprValue *field_val = &struct_val->data.x_struct.fields[i];
+                    field_val->special = ConstValSpecialUndef;
+                    field_val->type = struct_type->data.structure.fields[i].type_entry;
+                    ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
+                    if (parent != nullptr) {
+                        parent->id = ConstParentIdStruct;
+                        parent->data.p_struct.struct_val = struct_val;
+                        parent->data.p_struct.field_index = i;
+                    }
+                }
+            }
+            IrInstruction *result;
+            if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
+                result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope,
+                        source_instr->source_node, struct_ptr, field);
+                result->value.type = ptr_type;
+                result->value.special = ConstValSpecialStatic;
+            } else {
+                result = ir_const(ira, source_instr, ptr_type);
+            }
+            ConstExprValue *const_val = &result->value;
+            const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct;
+            const_val->data.x_ptr.mut = struct_ptr->value.data.x_ptr.mut;
+            const_val->data.x_ptr.data.base_struct.struct_val = struct_val;
+            const_val->data.x_ptr.data.base_struct.field_index = field->src_index;
+            return result;
+        }
+    }
+    IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node,
+            struct_ptr, field);
+    result->value.type = ptr_type;
+    return result;
+}
+
 static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
     IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initializing)
 {
@@ -17101,59 +17174,10 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
         return ira->codegen->invalid_instruction;
 
     assert(container_ptr->value.type->id == ZigTypeIdPointer);
-    bool is_const = container_ptr->value.type->data.pointer.is_const;
-    bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
     if (bare_type->id == ZigTypeIdStruct) {
         TypeStructField *field = find_struct_type_field(bare_type, field_name);
-        if (field) {
-            switch (type_has_one_possible_value(ira->codegen, field->type_entry)) {
-                case OnePossibleValueInvalid:
-                    return ira->codegen->invalid_instruction;
-                case OnePossibleValueYes: {
-                    IrInstruction *elem = ir_const(ira, source_instr, field->type_entry);
-                    return ir_get_ref(ira, source_instr, elem, false, false);
-                }
-                case OnePossibleValueNo:
-                    break;
-            }
-            bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked);
-            uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry);
-            uint32_t ptr_bit_offset = container_ptr->value.type->data.pointer.bit_offset_in_host;
-            uint32_t ptr_host_int_bytes = container_ptr->value.type->data.pointer.host_int_bytes;
-            uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ?
-                get_host_int_bytes(ira->codegen, bare_type, field) : ptr_host_int_bytes;
-            if (instr_is_comptime(container_ptr)) {
-                ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
-                if (!ptr_val)
-                    return ira->codegen->invalid_instruction;
-
-                if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
-                    ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
-                    if (struct_val == nullptr)
-                        return ira->codegen->invalid_instruction;
-                    if (type_is_invalid(struct_val->type))
-                        return ira->codegen->invalid_instruction;
-                    ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
-                            is_const, is_volatile, PtrLenSingle, align_bytes,
-                            (uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
-                            (uint32_t)host_int_bytes_for_result_type, false);
-                    IrInstruction *result = ir_const(ira, source_instr, ptr_type);
-                    ConstExprValue *const_val = &result->value;
-                    const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct;
-                    const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut;
-                    const_val->data.x_ptr.data.base_struct.struct_val = struct_val;
-                    const_val->data.x_ptr.data.base_struct.field_index = field->src_index;
-                    return result;
-                }
-            }
-            IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node,
-                    container_ptr, field);
-            result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
-                    PtrLenSingle,
-                    align_bytes,
-                    (uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
-                    host_int_bytes_for_result_type, false);
-            return result;
+        if (field != nullptr) {
+            return ir_analyze_struct_field_ptr(ira, source_instr, field, container_ptr, bare_type, initializing);
         } else {
             return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
                 source_instr, container_ptr, container_type);
@@ -17162,6 +17186,9 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
         return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
             source_instr, container_ptr, container_type);
     } else if (bare_type->id == ZigTypeIdUnion) {
+        bool is_const = container_ptr->value.type->data.pointer.is_const;
+        bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
+
         TypeUnionField *field = find_union_type_field(bare_type, field_name);
         if (field) {
             if (instr_is_comptime(container_ptr)) {
@@ -18849,7 +18876,7 @@ static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRe
 
 static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
     ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields,
-    IrInstruction *old_result_loc)
+    IrInstruction *result_loc)
 {
     Error err;
     assert(container_type->id == ZigTypeIdUnion);
@@ -18905,21 +18932,17 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI
         return result;
     }
 
-    ir_assert(old_result_loc != nullptr, instruction);
-    IrInstruction *result_loc = old_result_loc->child;
-    if (type_is_invalid(result_loc->value.type))
-        return result_loc;
     return ir_get_deref(ira, instruction, result_loc, nullptr);
 }
 
 static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
     ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields,
-    IrInstruction *old_result_loc)
+    IrInstruction *result_loc)
 {
     Error err;
     if (container_type->id == ZigTypeIdUnion) {
         return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count,
-                fields, old_result_loc);
+                fields, result_loc);
     }
     if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) {
         ir_add_error(ira, instruction,
@@ -18936,20 +18959,28 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
     IrInstruction *first_non_const_instruction = nullptr;
 
     AstNode **field_assign_nodes = allocate<AstNode *>(actual_field_count);
+    ZigList<IrInstruction *> const_ptrs = {};
 
     bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
         || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
 
-    ConstExprValue const_val = {};
-    const_val.special = ConstValSpecialStatic;
-    const_val.type = container_type;
-    // const_val.global_refs = allocate<ConstGlobalRefs>(1);
-    const_val.data.x_struct.fields = create_const_vals(actual_field_count);
+
+    // Here we iterate over the fields that have been initialized, and emit
+    // compile errors for missing fields and duplicate fields.
+    // It is only now that we find out whether the struct initialization can be a comptime
+    // value, but we have already emitted runtime instructions for the fields that
+    // were initialized with runtime values, and have omitted instructions that would have
+    // initialized fields with comptime values.
+    // So now we must clean up this situation. If it turns out the struct initialization can
+    // be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst.
+    // Otherwise, we must emit instructions to runtime-initialize the fields that have
+    // comptime-known values.
+
     for (size_t i = 0; i < instr_field_count; i += 1) {
         IrInstructionContainerInitFieldsField *field = &fields[i];
 
-        IrInstruction *field_value = field->value->child;
-        if (type_is_invalid(field_value->value.type))
+        IrInstruction *field_result_loc = field->result_loc->child;
+        if (type_is_invalid(field_result_loc->value.type))
             return ira->codegen->invalid_instruction;
 
         TypeStructField *type_field = find_struct_type_field(container_type, field->name);
@@ -18963,10 +18994,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
         if (type_is_invalid(type_field->type_entry))
             return ira->codegen->invalid_instruction;
 
-        IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
-        if (casted_field_value == ira->codegen->invalid_instruction)
-            return ira->codegen->invalid_instruction;
-
         size_t field_index = type_field->src_index;
         AstNode *existing_assign_node = field_assign_nodes[field_index];
         if (existing_assign_node) {
@@ -18976,23 +19003,18 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
         }
         field_assign_nodes[field_index] = field->source_node;
 
-        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);
-                if (!field_val)
-                    return ira->codegen->invalid_instruction;
-
-                copy_const_val(&const_val.data.x_struct.fields[field_index], field_val, true);
-            } else {
-                first_non_const_instruction = casted_field_value;
-                const_val.special = ConstValSpecialRuntime;
-            }
+        if (instr_is_comptime(field_result_loc) &&
+            field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar)
+        {
+            const_ptrs.append(field_result_loc);
+        } else {
+            first_non_const_instruction = field_result_loc;
         }
     }
 
     bool any_missing = false;
     for (size_t i = 0; i < actual_field_count; i += 1) {
-        if (field_assign_nodes[i]) continue;
+        if (field_assign_nodes[i] != nullptr) continue;
 
         // look for a default field value
         TypeStructField *field = &container_type->data.structure.fields[i];
@@ -19018,44 +19040,41 @@ 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);
 
-        if (const_val.special == ConstValSpecialStatic) {
-            copy_const_val(&const_val.data.x_struct.fields[i], field->init_val, true);
+        IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, instruction, field, result_loc,
+                container_type, true);
+        ir_analyze_store_ptr(ira, instruction, field_ptr, runtime_inst);
+        if (instr_is_comptime(field_ptr) && field_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
+            const_ptrs.append(field_ptr);
+        } else {
+            first_non_const_instruction = result_loc;
         }
     }
     if (any_missing)
         return ira->codegen->invalid_instruction;
 
-    if (const_val.special == ConstValSpecialStatic) {
-        IrInstruction *result = ir_const(ira, instruction, nullptr);
-        ConstExprValue *out_val = &result->value;
-        copy_const_val(out_val, &const_val, false);
-        out_val->type = container_type;
-
-        for (size_t i = 0; i < instr_field_count; i += 1) {
-            ConstExprValue *field_val = &out_val->data.x_struct.fields[i];
-            ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
-            if (parent != nullptr) {
-                parent->id = ConstParentIdStruct;
-                parent->data.p_struct.field_index = i;
-                parent->data.p_struct.struct_val = out_val;
+    if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) {
+        if (const_ptrs.length == actual_field_count) {
+            result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
+        } else {
+            result_loc->value.special = ConstValSpecialRuntime;
+            for (size_t i = 0; i < const_ptrs.length; i += 1) {
+                IrInstruction *field_result_loc = const_ptrs.at(i);
+                IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc, nullptr);
+                field_result_loc->value.special = ConstValSpecialRuntime;
+                ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref);
             }
         }
-
-        return result;
     }
 
-    if (is_comptime) {
+    IrInstruction *result = ir_get_deref(ira, instruction, result_loc, nullptr);
+
+    if (is_comptime && !instr_is_comptime(result)) {
         ir_add_error_node(ira, first_non_const_instruction->source_node,
             buf_sprintf("unable to evaluate constant expression"));
         return ira->codegen->invalid_instruction;
     }
 
-
-    ir_assert(old_result_loc != nullptr, instruction);
-    IrInstruction *result_loc = old_result_loc->child;
-    if (type_is_invalid(result_loc->value.type))
-        return result_loc;
-    return ir_get_deref(ira, instruction, result_loc, nullptr);
+    return result;
 }
 
 static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
@@ -19074,8 +19093,11 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
             buf_sprintf("expected array type or [_], found slice"));
         return ira->codegen->invalid_instruction;
     } else if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) {
-        return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr,
-                instruction->result_loc);
+        ir_assert(instruction->result_loc != nullptr, &instruction->base);
+        IrInstruction *result_loc = instruction->result_loc->child;
+        if (type_is_invalid(result_loc->value.type))
+            return result_loc;
+        return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc);
     } else if (container_type->id == ZigTypeIdArray) {
         // array is same as slice init but we make a compile error if the length is wrong
         ZigType *child_type;
@@ -19199,8 +19221,13 @@ static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ir
     if (type_is_invalid(container_type))
         return ira->codegen->invalid_instruction;
 
+    ir_assert(instruction->result_loc != nullptr, &instruction->base);
+    IrInstruction *result_loc = instruction->result_loc->child;
+    if (type_is_invalid(result_loc->value.type))
+        return result_loc;
+
     return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
-        instruction->field_count, instruction->fields, instruction->result_loc);
+        instruction->field_count, instruction->fields, result_loc);
 }
 
 static IrInstruction *ir_analyze_instruction_compile_err(IrAnalyze *ira,
@@ -24390,17 +24417,7 @@ static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstruct
     if (type_is_invalid(value->value.type))
         return ira->codegen->invalid_instruction;
 
-    bool want_resolve_result;
-    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;
-    }
+    bool want_resolve_result = !instruction->result_loc->written;
     if (want_resolve_result) {
         IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
                 value->value.type, value);