Commit 6de33ded81

Andrew Kelley <superjoe30@gmail.com>
2017-04-23 19:22:36
make undefined as a constant value lazy
closes #268
1 parent 2ed4707
src/all_types.hpp
@@ -98,9 +98,17 @@ struct ConstStructValue {
     ConstParent parent;
 };
 
+enum ConstArraySpecial {
+    ConstArraySpecialNone,
+    ConstArraySpecialUndef,
+};
+
 struct ConstArrayValue {
-    ConstExprValue *elements;
-    ConstParent parent;
+    ConstArraySpecial special;
+    struct {
+        ConstExprValue *elements;
+        ConstParent parent;
+    } s_none;
 };
 
 enum ConstPtrSpecial {
src/analyze.cpp
@@ -2727,7 +2727,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
 
     if (g->verbose) {
         fprintf(stderr, "{ // (analyzed)\n");
-        ir_print(stderr, &fn_table_entry->analyzed_executable, 4);
+        ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);
         fprintf(stderr, "}\n");
     }
 
@@ -2766,9 +2766,9 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
     }
     if (g->verbose) {
         fprintf(stderr, "\n");
-        ast_render(stderr, fn_table_entry->body_node, 4);
+        ast_render(g, stderr, fn_table_entry->body_node, 4);
         fprintf(stderr, "\n{ // (IR)\n");
-        ir_print(stderr, &fn_table_entry->ir_executable, 4);
+        ir_print(g, stderr, &fn_table_entry->ir_executable, 4);
         fprintf(stderr, "}\n");
     }
 
@@ -3355,10 +3355,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
 void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
     const_val->special = ConstValSpecialStatic;
     const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str));
-    const_val->data.x_array.elements = allocate<ConstExprValue>(buf_len(str));
+    const_val->data.x_array.s_none.elements = allocate<ConstExprValue>(buf_len(str));
 
     for (size_t i = 0; i < buf_len(str); i += 1) {
-        ConstExprValue *this_char = &const_val->data.x_array.elements[i];
+        ConstExprValue *this_char = &const_val->data.x_array.s_none.elements[i];
         this_char->special = ConstValSpecialStatic;
         this_char->type = g->builtin_types.entry_u8;
         bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]);
@@ -3377,14 +3377,14 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
     ConstExprValue *array_val = allocate<ConstExprValue>(1);
     array_val->special = ConstValSpecialStatic;
     array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null);
-    array_val->data.x_array.elements = allocate<ConstExprValue>(len_with_null);
+    array_val->data.x_array.s_none.elements = allocate<ConstExprValue>(len_with_null);
     for (size_t i = 0; i < buf_len(str); i += 1) {
-        ConstExprValue *this_char = &array_val->data.x_array.elements[i];
+        ConstExprValue *this_char = &array_val->data.x_array.s_none.elements[i];
         this_char->special = ConstValSpecialStatic;
         this_char->type = g->builtin_types.entry_u8;
         bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]);
     }
-    ConstExprValue *null_char = &array_val->data.x_array.elements[len_with_null - 1];
+    ConstExprValue *null_char = &array_val->data.x_array.s_none.elements[len_with_null - 1];
     null_char->special = ConstValSpecialStatic;
     null_char->type = g->builtin_types.entry_u8;
     bignum_init_unsigned(&null_char->data.x_bignum, 0);
@@ -3564,19 +3564,7 @@ void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
     TypeTableEntry *wanted_type = const_val->type;
     if (wanted_type->id == TypeTableEntryIdArray) {
         const_val->special = ConstValSpecialStatic;
-        size_t elem_count = wanted_type->data.array.len;
-        const_val->data.x_array.elements = allocate<ConstExprValue>(elem_count);
-        for (size_t i = 0; i < elem_count; i += 1) {
-            ConstExprValue *element_val = &const_val->data.x_array.elements[i];
-            element_val->type = wanted_type->data.array.child_type;
-            init_const_undefined(g, element_val);
-            ConstParent *parent = get_const_val_parent(element_val);
-            if (parent != nullptr) {
-                parent->id = ConstParentIdArray;
-                parent->data.p_array.array_val = const_val;
-                parent->data.p_array.elem_index = i;
-            }
-        }
+        const_val->data.x_array.special = ConstArraySpecialUndef;
     } else if (wanted_type->id == TypeTableEntryIdStruct) {
         ensure_complete_type(g, wanted_type);
 
@@ -3588,7 +3576,7 @@ void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
             field_val->type = wanted_type->data.structure.fields[i].type_entry;
             assert(field_val->type);
             init_const_undefined(g, field_val);
-            ConstParent *parent = get_const_val_parent(field_val);
+            ConstParent *parent = get_const_val_parent(g, field_val);
             if (parent != nullptr) {
                 parent->id = ConstParentIdStruct;
                 parent->data.p_struct.struct_val = const_val;
@@ -3802,7 +3790,7 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
     }
 }
 
-void render_const_value(Buf *buf, ConstExprValue *const_val) {
+void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
     switch (const_val->special) {
         case ConstValSpecialRuntime:
             buf_appendf(buf, "(runtime value)");
@@ -3872,7 +3860,7 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
                 case ConstPtrSpecialRef:
                 case ConstPtrSpecialBaseStruct:
                     buf_appendf(buf, "&");
-                    render_const_value(buf, const_ptr_pointee(const_val));
+                    render_const_value(g, buf, const_ptr_pointee(g, const_val));
                     return;
                 case ConstPtrSpecialBaseArray:
                     if (const_val->data.x_ptr.data.base_array.is_cstr) {
@@ -3880,7 +3868,7 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
                         return;
                     } else {
                         buf_appendf(buf, "&");
-                        render_const_value(buf, const_ptr_pointee(const_val));
+                        render_const_value(g, buf, const_ptr_pointee(g, const_val));
                         return;
                     }
                 case ConstPtrSpecialHardCodedAddr:
@@ -3910,6 +3898,11 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
                 TypeTableEntry *child_type = type_entry->data.array.child_type;
                 uint64_t len = type_entry->data.array.len;
 
+                if (const_val->data.x_array.special == ConstArraySpecialUndef) {
+                    buf_append_str(buf, "undefined");
+                    return;
+                }
+
                 // if it's []u8, assume UTF-8 and output a string
                 if (child_type->id == TypeTableEntryIdInt &&
                     child_type->data.integral.bit_count == 8 &&
@@ -3917,7 +3910,7 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
                 {
                     buf_append_char(buf, '"');
                     for (uint64_t i = 0; i < len; i += 1) {
-                        ConstExprValue *child_value = &const_val->data.x_array.elements[i];
+                        ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i];
                         uint64_t big_c = child_value->data.x_bignum.data.x_uint;
                         assert(big_c <= UINT8_MAX);
                         uint8_t c = (uint8_t)big_c;
@@ -3935,8 +3928,8 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
                 for (uint64_t i = 0; i < len; i += 1) {
                     if (i != 0)
                         buf_appendf(buf, ",");
-                    ConstExprValue *child_value = &const_val->data.x_array.elements[i];
-                    render_const_value(buf, child_value);
+                    ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i];
+                    render_const_value(g, buf, child_value);
                 }
                 buf_appendf(buf, "}");
                 return;
@@ -3954,7 +3947,7 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
         case TypeTableEntryIdMaybe:
             {
                 if (const_val->data.x_maybe) {
-                    render_const_value(buf, const_val->data.x_maybe);
+                    render_const_value(g, buf, const_val->data.x_maybe);
                 } else {
                     buf_appendf(buf, "null");
                 }
@@ -4167,11 +4160,32 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
     zig_unreachable();
 }
 
-ConstParent *get_const_val_parent(ConstExprValue *value) {
+void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
+    assert(const_val->type->id == TypeTableEntryIdArray);
+    if (const_val->data.x_array.special == ConstArraySpecialUndef) {
+        const_val->data.x_array.special = ConstArraySpecialNone;
+        size_t elem_count = const_val->type->data.array.len;
+        const_val->data.x_array.s_none.elements = allocate<ConstExprValue>(elem_count);
+        for (size_t i = 0; i < elem_count; i += 1) {
+            ConstExprValue *element_val = &const_val->data.x_array.s_none.elements[i];
+            element_val->type = const_val->type->data.array.child_type;
+            init_const_undefined(g, element_val);
+            ConstParent *parent = get_const_val_parent(g, element_val);
+            if (parent != nullptr) {
+                parent->id = ConstParentIdArray;
+                parent->data.p_array.array_val = const_val;
+                parent->data.p_array.elem_index = i;
+            }
+        }
+    }
+}
+
+ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
     assert(value->type);
     TypeTableEntry *type_entry = value->type;
     if (type_entry->id == TypeTableEntryIdArray) {
-        return &value->data.x_array.parent;
+        expand_undef_array(g, value);
+        return &value->data.x_array.s_none.parent;
     } else if (type_entry->id == TypeTableEntryIdStruct) {
         return &value->data.x_struct.parent;
     }
src/analyze.hpp
@@ -84,7 +84,7 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
 int64_t min_signed_val(TypeTableEntry *type_entry);
 uint64_t max_unsigned_val(TypeTableEntry *type_entry);
 
-void render_const_value(Buf *buf, ConstExprValue *const_val);
+void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val);
 void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars);
 void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node);
 
@@ -145,8 +145,9 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
 void init_const_undefined(CodeGen *g, ConstExprValue *const_val);
 
 TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
-ConstParent *get_const_val_parent(ConstExprValue *value);
+ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value);
 FnTableEntry *get_extern_panic_fn(CodeGen *g);
 TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type);
+void expand_undef_array(CodeGen *g, ConstExprValue *const_val);
 
 #endif
src/ast_render.cpp
@@ -273,6 +273,7 @@ void ast_print(FILE *f, AstNode *node, int indent) {
 
 
 struct AstRender {
+    CodeGen *codegen;
     int indent;
     int indent_size;
     FILE *f;
@@ -924,8 +925,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
 }
 
 
-void ast_render(FILE *f, AstNode *node, int indent_size) {
+void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size) {
     AstRender ar = {0};
+    ar.codegen = codegen;
     ar.f = f;
     ar.indent_size = indent_size;
     ar.indent = 0;
@@ -1030,15 +1032,16 @@ static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) {
     } else {
         Buf buf = BUF_INIT;
         buf_resize(&buf, 0);
-        render_const_value(&buf, var->value);
+        render_const_value(ar->codegen, &buf, var->value);
         fprintf(ar->f, "%s", buf_ptr(&buf));
     }
 
     fprintf(ar->f, ";\n");
 }
 
-void ast_render_decls(FILE *f, int indent_size, ImportTableEntry *import) {
+void ast_render_decls(CodeGen *codegen, FILE *f, int indent_size, ImportTableEntry *import) {
     AstRender ar = {0};
+    ar.codegen = codegen;
     ar.f = f;
     ar.indent_size = indent_size;
     ar.indent = 0;
src/ast_render.hpp
@@ -15,11 +15,11 @@
 
 void ast_print(FILE *f, AstNode *node, int indent);
 
-void ast_render(FILE *f, AstNode *node, int indent_size);
+void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size);
 
 const char *container_string(ContainerKind kind);
 
-void ast_render_decls(FILE *f, int indent_size, ImportTableEntry *import);
+void ast_render_decls(CodeGen *codegen, FILE *f, int indent_size, ImportTableEntry *import);
 
 #endif
 
src/codegen.cpp
@@ -3051,7 +3051,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
 }
 
 static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) {
-    ConstParent *parent = &array_const_val->data.x_array.parent;
+    expand_undef_array(g, array_const_val);
+    ConstParent *parent = &array_const_val->data.x_array.s_none.parent;
     LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent);
 
     TypeTableEntry *usize = g->builtin_types.entry_usize;
@@ -3283,9 +3284,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
         case TypeTableEntryIdArray:
             {
                 uint64_t len = type_entry->data.array.len;
+                if (const_val->data.x_array.special == ConstArraySpecialUndef) {
+                    return LLVMGetUndef(type_entry->type_ref);
+                }
+
                 LLVMValueRef *values = allocate<LLVMValueRef>(len);
                 for (uint64_t i = 0; i < len; i += 1) {
-                    ConstExprValue *elem_value = &const_val->data.x_array.elements[i];
+                    ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
                     values[i] = gen_const_val(g, elem_value);
                 }
                 return LLVMConstArray(LLVMTypeOf(values[0]), values, (unsigned)len);
@@ -4577,11 +4582,11 @@ static void define_builtin_compile_vars(CodeGen *g) {
         ConstExprValue *const_val = allocate<ConstExprValue>(1);
         const_val->special = ConstValSpecialStatic;
         const_val->type = get_array_type(g, str_type, g->link_libs.length);
-        const_val->data.x_array.elements = allocate<ConstExprValue>(g->link_libs.length);
+        const_val->data.x_array.s_none.elements = allocate<ConstExprValue>(g->link_libs.length);
         for (size_t i = 0; i < g->link_libs.length; i += 1) {
             Buf *link_lib_buf = g->link_libs.at(i);
             ConstExprValue *array_val = create_const_str_lit(g, link_lib_buf);
-            init_const_slice(g, &const_val->data.x_array.elements[i], array_val, 0, buf_len(link_lib_buf), true);
+            init_const_slice(g, &const_val->data.x_array.s_none.elements[i], array_val, 0, buf_len(link_lib_buf), true);
         }
 
         add_compile_var(g, "link_libs", const_val);
src/ir.cpp
@@ -56,7 +56,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc
 static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
 static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
 
-ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
+ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
     assert(const_val->type->id == TypeTableEntryIdPointer);
     assert(const_val->special == ConstValSpecialStatic);
     switch (const_val->data.x_ptr.special) {
@@ -65,7 +65,8 @@ ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
         case ConstPtrSpecialRef:
             return const_val->data.x_ptr.data.ref.pointee;
         case ConstPtrSpecialBaseArray:
-            return &const_val->data.x_ptr.data.base_array.array_val->data.x_array.elements[
+            expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val);
+            return &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[
                 const_val->data.x_ptr.data.base_array.elem_index];
         case ConstPtrSpecialBaseStruct:
             return &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[
@@ -5503,16 +5504,16 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
     return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
 }
 
-static bool render_instance_name_recursive(Buf *name, Scope *outer_scope, Scope *inner_scope) {
+static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) {
     if (inner_scope == nullptr || inner_scope == outer_scope) return false;
-    bool need_comma = render_instance_name_recursive(name, outer_scope, inner_scope->parent);
+    bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent);
     if (inner_scope->id != ScopeIdVarDecl)
         return need_comma;
 
     ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
     if (need_comma)
         buf_append_char(name, ',');
-    render_const_value(name, var_scope->var->value);
+    render_const_value(codegen, name, var_scope->var->value);
     return true;
 }
 
@@ -5529,7 +5530,7 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
             name = buf_alloc();
             buf_append_buf(name, &fn_entry->symbol_name);
             buf_appendf(name, "(");
-            render_instance_name_recursive(name, &fn_entry->fndef_scope->base, irb->exec->begin_scope);
+            render_instance_name_recursive(irb->codegen, name, &fn_entry->fndef_scope->base, irb->exec->begin_scope);
             buf_appendf(name, ")");
         } else {
             name = buf_sprintf("(anonymous %s at %s:%zu:%zu)", container_string(kind),
@@ -6523,9 +6524,9 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node
 
     if (codegen->verbose) {
         fprintf(stderr, "\nSource: ");
-        ast_render(stderr, node, 4);
+        ast_render(codegen, stderr, node, 4);
         fprintf(stderr, "\n{ // (IR)\n");
-        ir_print(stderr, &ir_executable, 4);
+        ir_print(codegen, stderr, &ir_executable, 4);
         fprintf(stderr, "}\n");
     }
     IrExecutable analyzed_executable = {0};
@@ -6544,7 +6545,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node
 
     if (codegen->verbose) {
         fprintf(stderr, "{ // (analyzed)\n");
-        ir_print(stderr, &analyzed_executable, 4);
+        ir_print(codegen, stderr, &analyzed_executable, 4);
         fprintf(stderr, "}\n");
     }
 
@@ -7305,7 +7306,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
             if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
                 ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
             {
-                ConstExprValue *pointee = const_ptr_pointee(&ptr->value);
+                ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value);
                 if (pointee->special != ConstValSpecialRuntime) {
                     IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
                         source_instruction->source_node, child_type);
@@ -7441,12 +7442,17 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
 
     assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
     ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
+    expand_undef_array(ira->codegen, array_val);
     size_t len = len_field->data.x_bignum.data.x_uint;
     Buf *result = buf_alloc();
     buf_resize(result, len);
     for (size_t i = 0; i < len; i += 1) {
         size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
-        ConstExprValue *char_val = &array_val->data.x_array.elements[new_index];
+        ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index];
+        if (char_val->special == ConstValSpecialUndef) {
+            ir_add_error(ira, casted_value, buf_sprintf("use of undefined value"));
+            return nullptr;
+        }
         uint64_t big_c = char_val->data.x_bignum.data.x_uint;
         assert(big_c <= UINT8_MAX);
         uint8_t c = (uint8_t)big_c;
@@ -8007,17 +8013,19 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *
         out_val->data.x_ptr.data.base_array.array_val = out_array_val;
         out_val->data.x_ptr.data.base_array.elem_index = 0;
     }
-    out_array_val->data.x_array.elements = allocate<ConstExprValue>(new_len);
+    out_array_val->data.x_array.s_none.elements = allocate<ConstExprValue>(new_len);
+
+    expand_undef_array(ira->codegen, op1_array_val);
 
     size_t next_index = 0;
     for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) {
-        out_array_val->data.x_array.elements[next_index] = op1_array_val->data.x_array.elements[i];
+        out_array_val->data.x_array.s_none.elements[next_index] = op1_array_val->data.x_array.s_none.elements[i];
     }
     for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
-        out_array_val->data.x_array.elements[next_index] = op2_array_val->data.x_array.elements[i];
+        out_array_val->data.x_array.s_none.elements[next_index] = op2_array_val->data.x_array.s_none.elements[i];
     }
     if (next_index < new_len) {
-        ConstExprValue *null_byte = &out_array_val->data.x_array.elements[next_index];
+        ConstExprValue *null_byte = &out_array_val->data.x_array.s_none.elements[next_index];
         init_const_unsigned_negative(null_byte, child_type, 0, false);
         next_index += 1;
     }
@@ -8061,12 +8069,14 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
 
     uint64_t new_array_len = array_len.data.x_uint;
-    out_val->data.x_array.elements = allocate<ConstExprValue>(new_array_len);
+    out_val->data.x_array.s_none.elements = allocate<ConstExprValue>(new_array_len);
+
+    expand_undef_array(ira->codegen, array_val);
 
     uint64_t i = 0;
     for (uint64_t x = 0; x < mult_amt; x += 1) {
         for (uint64_t y = 0; y < old_array_len; y += 1) {
-            out_val->data.x_array.elements[i] = array_val->data.x_array.elements[y];
+            out_val->data.x_array.s_none.elements[i] = array_val->data.x_array.s_none.elements[y];
             i += 1;
         }
     }
@@ -8848,7 +8858,7 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp
     // one of the ptr instructions
 
     if (instr_is_comptime(value)) {
-        ConstExprValue *pointee = const_ptr_pointee(&value->value);
+        ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value);
         if (pointee->type == child_type) {
             ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base);
             *out_val = *pointee;
@@ -9222,7 +9232,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
         ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
         if (!ptr_val)
             return ira->codegen->builtin_types.entry_invalid;
-        ConstExprValue *args_val = const_ptr_pointee(ptr_val);
+        ConstExprValue *args_val = const_ptr_pointee(ira->codegen, ptr_val);
         size_t start = args_val->data.x_arg_tuple.start_index;
         size_t end = args_val->data.x_arg_tuple.end_index;
         ConstExprValue *elem_index_val = ir_resolve_const(ira, elem_index, UndefBad);
@@ -9276,7 +9286,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
         ConstExprValue *array_ptr_val;
         if (array_ptr->value.special != ConstValSpecialRuntime &&
             array_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar &&
-            (array_ptr_val = const_ptr_pointee(&array_ptr->value)) &&
+            (array_ptr_val = const_ptr_pointee(ira->codegen, &array_ptr->value)) &&
             array_ptr_val->special != ConstValSpecialRuntime &&
             (array_type->id != TypeTableEntryIdPointer ||
                 array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr))
@@ -9431,7 +9441,7 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
                     return ira->codegen->builtin_types.entry_invalid;
 
                 if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
-                    ConstExprValue *struct_val = const_ptr_pointee(ptr_val);
+                    ConstExprValue *struct_val = const_ptr_pointee(ira->codegen, ptr_val);
                     ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index];
                     TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type,
                             is_const, is_volatile, 0, 0);
@@ -9565,7 +9575,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
             return ira->codegen->builtin_types.entry_invalid;
 
         assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
-        ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
+        ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val);
 
         if (buf_eql_str(field_name, "len")) {
             ConstExprValue *len_val = allocate<ConstExprValue>(1);
@@ -9594,7 +9604,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
             assert(ptr_type->id == TypeTableEntryIdPointer);
             child_type = ptr_type->data.pointer.child_type;
         } else if (container_ptr->value.type->id == TypeTableEntryIdPointer) {
-            ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
+            ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val);
             child_type = child_val->data.x_type;
         } else {
             zig_unreachable();
@@ -9688,7 +9698,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
         if (!container_ptr_val)
             return ira->codegen->builtin_types.entry_invalid;
 
-        ConstExprValue *namespace_val = const_ptr_pointee(container_ptr_val);
+        ConstExprValue *namespace_val = const_ptr_pointee(ira->codegen, container_ptr_val);
         assert(namespace_val->special == ConstValSpecialStatic);
 
         ImportTableEntry *namespace_import = namespace_val->data.x_import;
@@ -9761,7 +9771,7 @@ static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstru
         }
         if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) {
             if (instr_is_comptime(casted_value)) {
-                ConstExprValue *dest_val = const_ptr_pointee(&ptr->value);
+                ConstExprValue *dest_val = const_ptr_pointee(ira->codegen, &ptr->value);
                 if (dest_val->special != ConstValSpecialRuntime) {
                     *dest_val = casted_value->value;
                     if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) {
@@ -10630,7 +10640,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
     TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
     ConstExprValue *pointee_val = nullptr;
     if (instr_is_comptime(target_value_ptr)) {
-        pointee_val = const_ptr_pointee(&target_value_ptr->value);
+        pointee_val = const_ptr_pointee(ira->codegen, &target_value_ptr->value);
         if (pointee_val->special == ConstValSpecialRuntime)
             pointee_val = nullptr;
     }
@@ -10730,7 +10740,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr
             if (!target_value_ptr)
                 return ira->codegen->builtin_types.entry_invalid;
 
-            ConstExprValue *pointee_val = const_ptr_pointee(target_val_ptr);
+            ConstExprValue *pointee_val = const_ptr_pointee(ira->codegen, target_val_ptr);
             if (pointee_val->type->id == TypeTableEntryIdEnum) {
                 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
                 out_val->data.x_ptr.special = ConstPtrSpecialRef;
@@ -10982,7 +10992,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
 
         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(field_val);
+            ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
             if (parent != nullptr) {
                 parent->id = ConstParentIdStruct;
                 parent->data.p_struct.field_index = i;
@@ -11031,7 +11041,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira
             ConstExprValue const_val = {};
             const_val.special = ConstValSpecialStatic;
             const_val.type = fixed_size_array_type;
-            const_val.data.x_array.elements = allocate<ConstExprValue>(elem_count);
+            const_val.data.x_array.s_none.elements = allocate<ConstExprValue>(elem_count);
 
             bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
 
@@ -11056,7 +11066,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira
                         if (!elem_val)
                             return ira->codegen->builtin_types.entry_invalid;
 
-                        const_val.data.x_array.elements[i] = *elem_val;
+                        const_val.data.x_array.s_none.elements[i] = *elem_val;
                     } else {
                         first_non_const_instruction = casted_arg;
                         const_val.special = ConstValSpecialRuntime;
@@ -11068,8 +11078,8 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira
                 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
                 *out_val = const_val;
                 for (size_t i = 0; i < elem_count; i += 1) {
-                    ConstExprValue *elem_val = &out_val->data.x_array.elements[i];
-                    ConstParent *parent = get_const_val_parent(elem_val);
+                    ConstExprValue *elem_val = &out_val->data.x_array.s_none.elements[i];
+                    ConstParent *parent = get_const_val_parent(ira->codegen, elem_val);
                     if (parent != nullptr) {
                         parent->id = ConstParentIdArray;
                         parent->data.p_array.array_val = out_val;
@@ -11254,7 +11264,7 @@ static TypeTableEntry *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInst
         if (type_is_invalid(msg->value.type))
             return ira->codegen->builtin_types.entry_invalid;
         buf_resize(&buf, 0);
-        render_const_value(&buf, &msg->value);
+        render_const_value(ira->codegen, &buf, &msg->value);
         const char *comma_str = (i != 0) ? ", " : "";
         fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf));
     }
@@ -11526,7 +11536,7 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc
     if (ira->codegen->verbose) {
         fprintf(stderr, "\nC imports:\n");
         fprintf(stderr, "-----------\n");
-        ast_render_decls(stderr, 4, child_import);
+        ast_render_decls(ira->codegen, stderr, 4, child_import);
     }
 
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
@@ -11925,7 +11935,8 @@ static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructi
             case ConstPtrSpecialBaseArray:
                 {
                     ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
-                    dest_elements = array_val->data.x_array.elements;
+                    expand_undef_array(ira->codegen, array_val);
+                    dest_elements = array_val->data.x_array.s_none.elements;
                     start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
                     bound_end = array_val->type->data.array.len;
                     break;
@@ -12016,7 +12027,8 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi
             case ConstPtrSpecialBaseArray:
                 {
                     ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
-                    dest_elements = array_val->data.x_array.elements;
+                    expand_undef_array(ira->codegen, array_val);
+                    dest_elements = array_val->data.x_array.s_none.elements;
                     dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
                     dest_end = array_val->type->data.array.len;
                     break;
@@ -12049,7 +12061,8 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi
             case ConstPtrSpecialBaseArray:
                 {
                     ConstExprValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val;
-                    src_elements = array_val->data.x_array.elements;
+                    expand_undef_array(ira->codegen, array_val);
+                    src_elements = array_val->data.x_array.s_none.elements;
                     src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index;
                     src_end = array_val->type->data.array.len;
                     break;
@@ -12138,12 +12151,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
         size_t abs_offset;
         size_t rel_end;
         if (array_type->id == TypeTableEntryIdArray) {
-            array_val = const_ptr_pointee(&ptr_ptr->value);
+            array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
             abs_offset = 0;
             rel_end = array_type->data.array.len;
             parent_ptr = nullptr;
         } else if (array_type->id == TypeTableEntryIdPointer) {
-            parent_ptr = const_ptr_pointee(&ptr_ptr->value);
+            parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
             switch (parent_ptr->data.x_ptr.special) {
                 case ConstPtrSpecialInvalid:
                 case ConstPtrSpecialDiscard:
@@ -12165,7 +12178,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
                     break;
             }
         } else if (is_slice(array_type)) {
-            ConstExprValue *slice_ptr = const_ptr_pointee(&ptr_ptr->value);
+            ConstExprValue *slice_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
             parent_ptr = &slice_ptr->data.x_struct.fields[slice_ptr_index];
             ConstExprValue *len_val = &slice_ptr->data.x_struct.fields[slice_len_index];
 
@@ -12371,7 +12384,7 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst
         ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
         BigNum *op1_bignum = &casted_op1->value.data.x_bignum;
         BigNum *op2_bignum = &casted_op2->value.data.x_bignum;
-        ConstExprValue *pointee_val = const_ptr_pointee(&casted_result_ptr->value);
+        ConstExprValue *pointee_val = const_ptr_pointee(ira->codegen, &casted_result_ptr->value);
         BigNum *dest_bignum = &pointee_val->data.x_bignum;
         switch (instruction->op) {
             case IrOverflowOpAdd:
@@ -12455,7 +12468,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
             ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
             if (!ptr_val)
                 return ira->codegen->builtin_types.entry_invalid;
-            ConstExprValue *err_union_val = const_ptr_pointee(ptr_val);
+            ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val);
             if (err_union_val->special != ConstValSpecialRuntime) {
                 ErrorTableEntry *err = err_union_val->data.x_err_union.err;
                 assert(err);
@@ -12498,7 +12511,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
             ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
             if (!ptr_val)
                 return ira->codegen->builtin_types.entry_invalid;
-            ConstExprValue *err_union_val = const_ptr_pointee(ptr_val);
+            ConstExprValue *err_union_val = const_ptr_pointee(ira->codegen, ptr_val);
             if (err_union_val->special != ConstValSpecialRuntime) {
                 ErrorTableEntry *err = err_union_val->data.x_err_union.err;
                 if (err != nullptr) {
@@ -13248,7 +13261,7 @@ FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableE
 
     if (codegen->verbose) {
         fprintf(stderr, "{\n");
-        ir_print(stderr, &fn_entry->ir_executable, 4);
+        ir_print(codegen, stderr, &fn_entry->ir_executable, 4);
         fprintf(stderr, "}\n");
     }
 
src/ir.hpp
@@ -22,7 +22,7 @@ TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutabl
         TypeTableEntry *expected_type, AstNode *expected_type_source_node);
 
 bool ir_has_side_effects(IrInstruction *instruction);
-ConstExprValue *const_ptr_pointee(ConstExprValue *const_val);
+ConstExprValue *const_ptr_pointee(CodeGen *codegen, ConstExprValue *const_val);
 
 FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableEntry *var, Scope *parent_scope);
 
src/ir_print.cpp
@@ -10,6 +10,7 @@
 #include "ir_print.hpp"
 
 struct IrPrint {
+    CodeGen *codegen;
     FILE *f;
     int indent;
     int indent_size;
@@ -34,7 +35,7 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) {
 static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {
     Buf buf = BUF_INIT;
     buf_resize(&buf, 0);
-    render_const_value(&buf, const_val);
+    render_const_value(irp->codegen, &buf, const_val);
     fprintf(irp->f, "%s", buf_ptr(&buf));
 }
 
@@ -1188,9 +1189,10 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     fprintf(irp->f, "\n");
 }
 
-void ir_print(FILE *f, IrExecutable *executable, int indent_size) {
+void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size) {
     IrPrint ir_print = {};
     IrPrint *irp = &ir_print;
+    irp->codegen = codegen;
     irp->f = f;
     irp->indent = indent_size;
     irp->indent_size = indent_size;
src/ir_print.hpp
@@ -12,6 +12,6 @@
 
 #include <stdio.h>
 
-void ir_print(FILE *f, IrExecutable *executable, int indent_size);
+void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size);
 
 #endif
src/main.cpp
@@ -609,7 +609,7 @@ int main(int argc, char **argv) {
                 return EXIT_SUCCESS;
             } else if (cmd == CmdParseH) {
                 codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code);
-                ast_render_decls(stdout, 4, g->root_import);
+                ast_render_decls(g, stdout, 4, g->root_import);
                 return EXIT_SUCCESS;
             } else if (cmd == CmdTest) {
                 codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);