diff options
| author | Cody Tapscott <topolarity@tapscott.me> | 2022-11-01 09:22:31 -0700 |
|---|---|---|
| committer | Cody Tapscott <topolarity@tapscott.me> | 2022-11-10 12:22:37 -0700 |
| commit | ff699722da1f2df3e521c92cebe71c50910594d3 (patch) | |
| tree | f96c8f78322a864ade0c704dfcaf09c241bf919c | |
| parent | 59af6417bbb93a2cca453d930320217a970040bd (diff) | |
| download | zig-ff699722da1f2df3e521c92cebe71c50910594d3.tar.gz zig-ff699722da1f2df3e521c92cebe71c50910594d3.zip | |
stage2: Fix comptime array initialization
This is a follow-up to 9dc98fba, which made comptime initialization
patterns for union/struct more robust, especially when storing to
comptime-known pointers (and globals).
Resolves #13063.
| -rw-r--r-- | src/Sema.zig | 111 | ||||
| -rw-r--r-- | test/behavior.zig | 1 | ||||
| -rw-r--r-- | test/behavior/bugs/13063.zig | 16 |
3 files changed, 68 insertions, 60 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 2704012b57..d2f530702f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4164,6 +4164,7 @@ fn validateStructInit( // We expect to see something like this in the current block AIR: // %a = field_ptr(...) // store(%a, %b) + // With an optional bitcast between the store and the field_ptr. // If %b is a comptime operand, this field is comptime. // // However, in the case of a comptime-known pointer to a struct, the @@ -4374,75 +4375,65 @@ fn zirValidateArrayInit( const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?; const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?; - // Find the block index of the elem_ptr so that we can look at the next - // instruction after it within the same block. + + // We expect to see something like this in the current block AIR: + // %a = elem_ptr(...) + // store(%a, %b) + // With an optional bitcast between the store and the elem_ptr. + // If %b is a comptime operand, this element is comptime. + // + // However, in the case of a comptime-known pointer to an array, the + // the elem_ptr instruction is missing, so we have to pattern-match + // based only on the store instructions. + // `first_block_index` needs to point to the `elem_ptr` if it exists; + // the `store` otherwise. + // + // It's also possible for there to be no store instruction, in the case + // of nested `coerce_result_ptr` instructions. If we see the `elem_ptr` + // but we have not found a `store`, treat as a runtime-known element. + // + // This is nearly identical to similar logic in `validateStructInit`. + // Possible performance enhancement: save the `block_index` between iterations // of the for loop. var block_index = block.instructions.items.len - 1; - while (block.instructions.items[block_index] != elem_ptr_air_inst) { - if (block_index == 0) { + while (block_index > 0) : (block_index -= 1) { + const store_inst = block.instructions.items[block_index]; + if (store_inst == elem_ptr_air_inst) { array_is_comptime = false; continue :outer; } - block_index -= 1; - } - first_block_index = @min(first_block_index, block_index); - - // If the next instructon is a store with a comptime operand, this element - // is comptime. - const next_air_inst = block.instructions.items[block_index + 1]; - switch (air_tags[next_air_inst]) { - .store => { - const bin_op = air_datas[next_air_inst].bin_op; - var lhs = bin_op.lhs; - if (Air.refToIndex(lhs)) |lhs_index| { - if (air_tags[lhs_index] == .bitcast) { - lhs = air_datas[lhs_index].ty_op.operand; - block_index -= 1; - } - } - if (lhs != elem_ptr_air_ref) { - array_is_comptime = false; - continue; - } - if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| { - element_vals[i] = val; - } else { - array_is_comptime = false; - } - continue; - }, - .bitcast => { - // %a = bitcast(*arr_ty, %array_base) - // %b = ptr_elem_ptr(%a, %index) - // %c = bitcast(*elem_ty, %b) - // %d = store(%c, %val) - if (air_datas[next_air_inst].ty_op.operand != elem_ptr_air_ref) { - array_is_comptime = false; - continue; - } - const store_inst = block.instructions.items[block_index + 2]; - if (air_tags[store_inst] != .store) { - array_is_comptime = false; - continue; - } - const bin_op = air_datas[store_inst].bin_op; - if (bin_op.lhs != Air.indexToRef(next_air_inst)) { - array_is_comptime = false; - continue; - } - if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| { - element_vals[i] = val; - } else { - array_is_comptime = false; + if (air_tags[store_inst] != .store) continue; + const bin_op = air_datas[store_inst].bin_op; + var lhs = bin_op.lhs; + { + const lhs_index = Air.refToIndex(lhs) orelse continue; + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; } - continue; - }, - else => { + } + if (lhs != elem_ptr_air_ref) continue; + while (block_index > 0) : (block_index -= 1) { + const block_inst = block.instructions.items[block_index - 1]; + if (air_tags[block_inst] != .dbg_stmt) break; + } + if (block_index > 0 and + elem_ptr_air_inst == block.instructions.items[block_index - 1]) + { + first_block_index = @min(first_block_index, block_index - 1); + } else { + first_block_index = @min(first_block_index, block_index); + } + if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| { + element_vals[i] = val; + } else { array_is_comptime = false; - continue; - }, + } + continue :outer; } + array_is_comptime = false; + continue :outer; } if (array_is_comptime) { diff --git a/test/behavior.zig b/test/behavior.zig index a92a5fcbf0..41c2a79f9e 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -210,6 +210,7 @@ test { builtin.zig_backend != .stage2_wasm and builtin.zig_backend != .stage2_c) { + _ = @import("behavior/bugs/13063.zig"); _ = @import("behavior/bugs/11227.zig"); _ = @import("behavior/export.zig"); } diff --git a/test/behavior/bugs/13063.zig b/test/behavior/bugs/13063.zig new file mode 100644 index 0000000000..4fa0ab9d83 --- /dev/null +++ b/test/behavior/bugs/13063.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const expect = std.testing.expect; + +var pos = [2]f32{ 0.0, 0.0 }; +test "store to global array" { + try expect(pos[1] == 0.0); + pos = [2]f32{ 0.0, 1.0 }; + try expect(pos[1] == 1.0); +} + +var vpos = @Vector(2, f32){ 0.0, 0.0 }; +test "store to global vector" { + try expect(vpos[1] == 0.0); + vpos = @Vector(2, f32){ 0.0, 1.0 }; + try expect(vpos[1] == 1.0); +} |
