Commit 1195994880

Andrew Kelley <superjoe30@gmail.com>
2017-02-27 06:05:08
fix inability to write to global in some cases
before, when we initialized a variable by copying the initialization value, it made the internal const value references point to a duplicate value, resulting in a phony duplicate global value being updated instead of the real on. now the behavior is as expected. thanks to hoppetosse for pointing out this bug on IRC.
1 parent 2576157
src/all_types.hpp
@@ -1430,7 +1430,7 @@ enum VarLinkage {
 
 struct VariableTableEntry {
     Buf name;
-    ConstExprValue value;
+    ConstExprValue *value;
     LLVMValueRef value_ref;
     bool src_is_const;
     bool gen_is_const;
src/analyze.cpp
@@ -2085,7 +2085,7 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent
     assert(value);
 
     VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
-    variable_entry->value = *value;
+    variable_entry->value = value;
     variable_entry->parent_scope = parent_scope;
     variable_entry->shadowable = false;
     variable_entry->mem_slot_index = SIZE_MAX;
@@ -2101,21 +2101,21 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent
             ErrorMsg *msg = add_node_error(g, source_node,
                     buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
             add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
-            variable_entry->value.type = g->builtin_types.entry_invalid;
+            variable_entry->value->type = g->builtin_types.entry_invalid;
         } else {
             auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
             if (primitive_table_entry) {
                 TypeTableEntry *type = primitive_table_entry->value;
                 add_node_error(g, source_node,
                         buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
-                variable_entry->value.type = g->builtin_types.entry_invalid;
+                variable_entry->value->type = g->builtin_types.entry_invalid;
             } else {
                 Tld *tld = find_decl(g, parent_scope, name);
                 if (tld && tld->id != TldIdVar) {
                     ErrorMsg *msg = add_node_error(g, source_node,
                             buf_sprintf("redefinition of '%s'", buf_ptr(name)));
                     add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here"));
-                    variable_entry->value.type = g->builtin_types.entry_invalid;
+                    variable_entry->value->type = g->builtin_types.entry_invalid;
                 }
             }
         }
@@ -3181,7 +3181,7 @@ uint32_t fn_eval_hash(Scope* scope) {
     while (scope) {
         if (scope->id == ScopeIdVarDecl) {
             ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
-            result += hash_const_val(&var_scope->var->value);
+            result += hash_const_val(var_scope->var->value);
         } else if (scope->id == ScopeIdFnDef) {
             ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
             result += hash_ptr(fn_scope->fn_entry);
@@ -3203,9 +3203,9 @@ bool fn_eval_eql(Scope *a, Scope *b) {
         if (a->id == ScopeIdVarDecl) {
             ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a;
             ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
-            if (a_var_scope->var->value.type != b_var_scope->var->value.type)
+            if (a_var_scope->var->value->type != b_var_scope->var->value->type)
                 return false;
-            if (!const_values_equal(&a_var_scope->var->value, &b_var_scope->var->value))
+            if (!const_values_equal(a_var_scope->var->value, b_var_scope->var->value))
                 return false;
         } else if (a->id == ScopeIdFnDef) {
             ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
src/ast_render.cpp
@@ -964,26 +964,26 @@ static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) {
     const char *extern_str = extern_string(var->linkage == VarLinkageExternal);
     fprintf(ar->f, "%s%s%s %s", visib_mod_str, extern_str, const_or_var, buf_ptr(name));
 
-    if (var->value.type->id == TypeTableEntryIdNumLitFloat ||
-        var->value.type->id == TypeTableEntryIdNumLitInt ||
-        var->value.type->id == TypeTableEntryIdMetaType)
+    if (var->value->type->id == TypeTableEntryIdNumLitFloat ||
+        var->value->type->id == TypeTableEntryIdNumLitInt ||
+        var->value->type->id == TypeTableEntryIdMetaType)
     {
         // skip type
     } else {
-        fprintf(ar->f, ": %s", buf_ptr(&var->value.type->name));
+        fprintf(ar->f, ": %s", buf_ptr(&var->value->type->name));
     }
 
-    if (var->value.special == ConstValSpecialRuntime) {
+    if (var->value->special == ConstValSpecialRuntime) {
         fprintf(ar->f, ";\n");
         return;
     }
 
     fprintf(ar->f, " = ");
 
-    if (var->value.special == ConstValSpecialStatic &&
-        var->value.type->id == TypeTableEntryIdMetaType)
+    if (var->value->special == ConstValSpecialStatic &&
+        var->value->type->id == TypeTableEntryIdMetaType)
     {
-        TypeTableEntry *type_entry = var->value.data.x_type;
+        TypeTableEntry *type_entry = var->value->data.x_type;
         if (type_entry->id == TypeTableEntryIdStruct) {
             const char *layout_str = layout_string(type_entry->data.structure.layout);
             fprintf(ar->f, "%sstruct {\n", layout_str);
@@ -1022,7 +1022,7 @@ 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(&buf, var->value);
         fprintf(ar->f, "%s", buf_ptr(&buf));
     }
 
src/codegen.cpp
@@ -1316,7 +1316,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
 {
     VariableTableEntry *var = decl_var_instruction->var;
 
-    if (!type_has_bits(var->value.type))
+    if (!type_has_bits(var->value->type))
         return nullptr;
 
     if (var->ref_count == 0 && g->is_release_build)
@@ -1331,16 +1331,16 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
         have_init_expr = true;
 
     if (have_init_expr) {
-        assert(var->value.type == init_value->value.type);
-        gen_assign_raw(g, var->value_ref, ir_llvm_value(g, init_value), var->value.type);
+        assert(var->value->type == init_value->value.type);
+        gen_assign_raw(g, var->value_ref, ir_llvm_value(g, init_value), var->value->type);
     } else {
         bool ignore_uninit = false;
         // handle runtime stack allocation
         bool want_safe = ir_want_debug_safety(g, &decl_var_instruction->base);
         if (!ignore_uninit && want_safe) {
             TypeTableEntry *usize = g->builtin_types.entry_usize;
-            uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value.type->type_ref);
-            uint64_t align_bytes = get_type_alignment(g, var->value.type);
+            uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref);
+            uint64_t align_bytes = get_type_alignment(g, var->value->type);
 
             // memset uninitialized memory to 0xa
             LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
@@ -1437,7 +1437,7 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir
 
 static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
     VariableTableEntry *var = instruction->var;
-    if (type_has_bits(var->value.type)) {
+    if (type_has_bits(var->value->type)) {
         assert(var->value_ref);
         return var->value_ref;
     } else {
@@ -3203,9 +3203,9 @@ static void do_code_gen(CodeGen *g) {
         TldVar *tld_var = g->global_vars.at(i);
         VariableTableEntry *var = tld_var->var;
 
-        if (var->value.type->id == TypeTableEntryIdNumLitFloat) {
+        if (var->value->type->id == TypeTableEntryIdNumLitFloat) {
             // Generate debug info for it but that's it.
-            ConstExprValue *const_val = &var->value;
+            ConstExprValue *const_val = var->value;
             assert(const_val->special != ConstValSpecialRuntime);
             TypeTableEntry *var_type = g->builtin_types.entry_f64;
             LLVMValueRef init_val = LLVMConstReal(var_type->type_ref, const_val->data.x_bignum.data.x_float);
@@ -3213,9 +3213,9 @@ static void do_code_gen(CodeGen *g) {
             continue;
         }
 
-        if (var->value.type->id == TypeTableEntryIdNumLitInt) {
+        if (var->value->type->id == TypeTableEntryIdNumLitInt) {
             // Generate debug info for it but that's it.
-            ConstExprValue *const_val = &var->value;
+            ConstExprValue *const_val = var->value;
             assert(const_val->special != ConstValSpecialRuntime);
             TypeTableEntry *var_type = const_val->data.x_bignum.is_negative ?
                 g->builtin_types.entry_isize : g->builtin_types.entry_usize;
@@ -3225,22 +3225,22 @@ static void do_code_gen(CodeGen *g) {
             continue;
         }
 
-        if (!type_has_bits(var->value.type))
+        if (!type_has_bits(var->value->type))
             continue;
 
         assert(var->decl_node);
 
         LLVMValueRef global_value;
         if (var->linkage == VarLinkageExternal) {
-            global_value = LLVMAddGlobal(g->module, var->value.type->type_ref, buf_ptr(&var->name));
+            global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name));
 
             // TODO debug info for the extern variable
 
             LLVMSetLinkage(global_value, LLVMExternalLinkage);
         } else {
-            render_const_val(g, &var->value);
-            render_const_val_global(g, &var->value, buf_ptr(&var->name));
-            global_value = var->value.llvm_global;
+            render_const_val(g, var->value);
+            render_const_val_global(g, var->value, buf_ptr(&var->name));
+            global_value = var->value->llvm_global;
 
             if (var->linkage == VarLinkageExport) {
                 LLVMSetLinkage(global_value, LLVMExternalLinkage);
@@ -3249,11 +3249,11 @@ static void do_code_gen(CodeGen *g) {
                 LLVMSetSection(global_value, buf_ptr(tld_var->section_name));
             }
             LLVMSetAlignment(global_value, tld_var->alignment ?
-                    tld_var->alignment : get_type_alignment(g, var->value.type));
+                    tld_var->alignment : get_type_alignment(g, var->value->type));
 
             // TODO debug info for function pointers
-            if (var->gen_is_const && var->value.type->id != TypeTableEntryIdFn) {
-                gen_global_var(g, var, var->value.llvm_value, var->value.type);
+            if (var->gen_is_const && var->value->type->id != TypeTableEntryIdFn) {
+                gen_global_var(g, var, var->value->llvm_value, var->value->type);
             }
         }
 
@@ -3432,30 +3432,30 @@ static void do_code_gen(CodeGen *g) {
         for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
             VariableTableEntry *var = fn_table_entry->variable_list.at(var_i);
 
-            if (!type_has_bits(var->value.type)) {
+            if (!type_has_bits(var->value->type)) {
                 continue;
             }
             if (ir_get_var_is_comptime(var))
                 continue;
-            if (type_requires_comptime(var->value.type))
+            if (type_requires_comptime(var->value->type))
                 continue;
 
             if (var->src_arg_index == SIZE_MAX) {
-                var->value_ref = build_alloca(g, var->value.type, buf_ptr(&var->name));
+                var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name));
 
                 var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
                         buf_ptr(&var->name), import->di_file, var->decl_node->line + 1,
-                        var->value.type->di_type, !g->strip_debug_symbols, 0);
+                        var->value->type->di_type, !g->strip_debug_symbols, 0);
 
             } else {
                 assert(var->gen_arg_index != SIZE_MAX);
                 TypeTableEntry *gen_type;
-                if (handle_is_ptr(var->value.type)) {
+                if (handle_is_ptr(var->value->type)) {
                     gen_type = fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index].type;
                     var->value_ref = LLVMGetParam(fn, var->gen_arg_index);
                 } else {
-                    gen_type = var->value.type;
-                    var->value_ref = build_alloca(g, var->value.type, buf_ptr(&var->name));
+                    gen_type = var->value->type;
+                    var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name));
                 }
                 if (var->decl_node) {
                     var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
@@ -3483,7 +3483,7 @@ static void do_code_gen(CodeGen *g) {
             assert(variable);
             assert(variable->value_ref);
 
-            if (!handle_is_ptr(variable->value.type)) {
+            if (!handle_is_ptr(variable->value->type)) {
                 clear_debug_source_node(g);
                 LLVMBuildStore(g->builder, LLVMGetParam(fn, variable->gen_arg_index), variable->value_ref);
             }
src/ir.cpp
@@ -3164,6 +3164,7 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco
     variable_entry->mem_slot_index = SIZE_MAX;
     variable_entry->is_comptime = is_comptime;
     variable_entry->src_arg_index = SIZE_MAX;
+    variable_entry->value = allocate<ConstExprValue>(1);
 
     if (name) {
         buf_init_from_buf(&variable_entry->name, name);
@@ -3173,21 +3174,21 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco
             ErrorMsg *msg = add_node_error(codegen, node,
                     buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
             add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
-            variable_entry->value.type = codegen->builtin_types.entry_invalid;
+            variable_entry->value->type = codegen->builtin_types.entry_invalid;
         } else {
             auto primitive_table_entry = codegen->primitive_type_table.maybe_get(name);
             if (primitive_table_entry) {
                 TypeTableEntry *type = primitive_table_entry->value;
                 add_node_error(codegen, node,
                         buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
-                variable_entry->value.type = codegen->builtin_types.entry_invalid;
+                variable_entry->value->type = codegen->builtin_types.entry_invalid;
             } else {
                 Tld *tld = find_decl(codegen, parent_scope, name);
                 if (tld && tld->id != TldIdVar) {
                     ErrorMsg *msg = add_node_error(codegen, node,
                             buf_sprintf("redefinition of '%s'", buf_ptr(name)));
                     add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
-                    variable_entry->value.type = codegen->builtin_types.entry_invalid;
+                    variable_entry->value->type = codegen->builtin_types.entry_invalid;
                 }
             }
         }
@@ -4516,7 +4517,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
     // is inside var->child_scope
 
     if (!is_extern && !variable_declaration->expr) {
-        var->value.type = irb->codegen->builtin_types.entry_invalid;
+        var->value->type = irb->codegen->builtin_types.entry_invalid;
         add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized"));
         return irb->codegen->invalid_instruction;
     }
@@ -5387,7 +5388,7 @@ static bool render_instance_name_recursive(Buf *name, Scope *outer_scope, Scope
     ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
     if (need_comma)
         buf_append_char(name, ',');
-    render_const_value(name, &var_scope->var->value);
+    render_const_value(name, var_scope->var->value);
     return true;
 }
 
@@ -7827,8 +7828,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
 
     IrInstruction *init_value = decl_var_instruction->init_value->other;
     if (type_is_invalid(init_value->value.type)) {
-        var->value.type = ira->codegen->builtin_types.entry_invalid;
-        return var->value.type;
+        var->value->type = ira->codegen->builtin_types.entry_invalid;
+        return var->value->type;
     }
 
     AstNodeVariableDeclaration *variable_declaration = &var->decl_node->data.variable_declaration;
@@ -7844,8 +7845,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
         TypeTableEntry *proposed_type = ir_resolve_type(ira, var_type);
         explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type);
         if (type_is_invalid(explicit_type)) {
-            var->value.type = ira->codegen->builtin_types.entry_invalid;
-            return var->value.type;
+            var->value->type = ira->codegen->builtin_types.entry_invalid;
+            return var->value->type;
         }
     }
 
@@ -7906,8 +7907,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
             break;
     }
 
-    var->value.type = result_type;
-    assert(var->value.type);
+    var->value->type = result_type;
+    assert(var->value->type);
 
     if (type_is_invalid(result_type)) {
         decl_var_instruction->base.other = &decl_var_instruction->base;
@@ -7930,7 +7931,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
     } else if (is_comptime) {
         ir_add_error(ira, &decl_var_instruction->base,
                 buf_sprintf("cannot store runtime value in compile time variable"));
-        var->value.type = ira->codegen->builtin_types.entry_invalid;
+        var->value->type = ira->codegen->builtin_types.entry_invalid;
         return ira->codegen->builtin_types.entry_invalid;
     }
 
@@ -8766,24 +8767,24 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
 static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
         VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr)
 {
-    assert(var->value.type);
-    if (type_is_invalid(var->value.type))
-        return var->value.type;
+    assert(var->value->type);
+    if (type_is_invalid(var->value->type))
+        return var->value->type;
 
     bool comptime_var_mem = ir_get_var_is_comptime(var);
 
     ConstExprValue *mem_slot = nullptr;
     FnTableEntry *fn_entry = scope_fn_entry(var->parent_scope);
-    if (var->value.special == ConstValSpecialStatic) {
-        mem_slot = &var->value;
+    if (var->value->special == ConstValSpecialStatic) {
+        mem_slot = var->value;
     } else if (fn_entry) {
         // TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing.
         if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const))
             mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
     }
 
-    bool is_const = (var->value.type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
-    bool is_volatile = (var->value.type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false;
+    bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
+    bool is_volatile = (var->value->type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false;
     if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
         ConstPtrMut ptr_mut;
         if (comptime_var_mem) {
@@ -8794,11 +8795,11 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc
             assert(!comptime_var_mem);
             ptr_mut = ConstPtrMutRuntimeVar;
         }
-        return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type, ptr_mut, is_const, is_volatile);
+        return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value->type, ptr_mut, is_const, is_volatile);
     } else {
         ir_build_var_ptr_from(&ira->new_irb, instruction, var, is_const, is_volatile);
-        type_ensure_zero_bits_known(ira->codegen, var->value.type);
-        return get_pointer_to_type(ira->codegen, var->value.type, var->src_is_const);
+        type_ensure_zero_bits_known(ira->codegen, var->value->type);
+        return get_pointer_to_type(ira->codegen, var->value->type, var->src_is_const);
     }
 }
 
@@ -12520,8 +12521,8 @@ FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableE
     fn_entry->fndef_scope = create_fndef_scope(nullptr, parent_scope, fn_entry);
     fn_entry->child_scope = &fn_entry->fndef_scope->base;
 
-    assert(var->value.type->id == TypeTableEntryIdMaybe);
-    TypeTableEntry *src_fn_type = var->value.type->data.maybe.child_type;
+    assert(var->value->type->id == TypeTableEntryIdMaybe);
+    TypeTableEntry *src_fn_type = var->value->type->data.maybe.child_type;
     assert(src_fn_type->id == TypeTableEntryIdFn);
 
     FnTypeId new_fn_type = src_fn_type->data.fn.fn_type_id;
src/parseh.cpp
@@ -1235,7 +1235,7 @@ static void process_symbol_macros(Context *c) {
         // variable is non-null and calls it.
         if (existing_tld->id == TldIdVar) {
             TldVar *tld_var = (TldVar *)existing_tld;
-            TypeTableEntry *var_type = tld_var->var->value.type;
+            TypeTableEntry *var_type = tld_var->var->value->type;
             if (var_type->id == TypeTableEntryIdMaybe && !tld_var->var->src_is_const) {
                 TypeTableEntry *child_type = var_type->data.maybe.child_type;
                 if (child_type->id == TypeTableEntryIdFn) {
test/cases/array.zig
@@ -72,24 +72,23 @@ fn nestedArrays() {
 }
 
 
-// TODO
-//var s_array: [8]Sub = undefined;
-//const Sub = struct {
-//    b: u8,
-//};
-//const Str = struct {
-//    a: []Sub,
-//};
-//fn setGlobalVarArrayViaSliceEmbeddedInStruct() {
-//    @setFnTest(this);
-//
-//    var s = Str { .a = s_array[0...]};
-//
-//    s.a[0].b = 1;
-//    s.a[1].b = 2;
-//    s.a[2].b = 3;
-//
-//    assert(s_array[0].b == 1);
-//    assert(s_array[1].b == 2);
-//    assert(s_array[2].b == 3);
-//}
+var s_array: [8]Sub = undefined;
+const Sub = struct {
+    b: u8,
+};
+const Str = struct {
+    a: []Sub,
+};
+fn setGlobalVarArrayViaSliceEmbeddedInStruct() {
+    @setFnTest(this);
+
+    var s = Str { .a = s_array[0...]};
+
+    s.a[0].b = 1;
+    s.a[1].b = 2;
+    s.a[2].b = 3;
+
+    assert(s_array[0].b == 1);
+    assert(s_array[1].b == 2);
+    assert(s_array[2].b == 3);
+}