zig
zig copied to clipboard
Unreachable code when calling functions on EnumMap to empty struct at comptime
Zig Version
0.11.0-dev.1907+ecc0108ce
Steps to Reproduce and Observed Behavior
Create an EnumMap
with an empty struct as value at comptime. Still at comptime, calling certain functions will cause the compiler to reach unreachable code at value.zig:2564:29
(hash
).
I have checked the following EnumMap
functions:
-
count
fails -
contains
fails -
put
works -
fetchPut
works
The issue can be reproduced by zig test
ing the following program. Uncomment the commented lines, and everything works as expected.
const std = @import("std");
const K = enum {
one,
};
const V = struct {
// number: u32,
};
const numbers = map: {
var map = std.EnumMap(K, V){};
map.put(.one, .{
// .number = 1,
});
_ = map.count();
break :map map;
};
test "comptime" {
_ = numbers;
}
test "runtime" {
var map = std.EnumMap(K, V){};
map.put(.one, .{
// .number = 1,
});
_ = map.count();
}
thread 26804 panic: reached unreachable code
Analyzing main.zig: main.zig:numbers
%12 = alloc_inferred_comptime_mut() node_offset:12:5 to :12:34
%13 = decl_ref("std") token_offset:12:15 to :12:18
%14 = field_call_bind(%13, "EnumMap") node_offset:12:15 to :12:26
%15 = dbg_stmt(2, 26)
%16 = call(.compile_time, %14, [
{
%17 = decl_val("K") token_offset:12:27 to :12:28
%18 = break_inline(%16, %17)
},
{
%19 = decl_val("V") token_offset:12:30 to :12:31
%20 = break_inline(%16, %19)
},
]) node_offset:12:15 to :12:32
%21 = struct_init_empty(%16) node_offset:12:15 to :12:34
%22 = store_to_inferred_ptr(%12, %21)
%23 = resolve_inferred_alloc(%12) node_offset:12:5 to :12:34
%24 = field_call_bind(%12, "put") node_offset:13:5 to :13:12
%25 = dbg_stmt(3, 12)
%26 = call(nodiscard .compile_time, %24, [
{
%27 = enum_literal("one") token_offset:13:14 to :13:17
%28 = break_inline(%26, %27)
},
{
%29 = break_inline(%26, @Zir.Inst.Ref.empty_struct)
},
]) node_offset:13:5 to :13:13
%30 = field_call_bind(%12, "count") node_offset:16:9 to :16:18
%31 = dbg_stmt(6, 18)
> %32 = call(.compile_time, %30, []) node_offset:16:9 to :16:20
%33 = ensure_result_non_error(%32) node_offset:16:9 to :16:20
%34 = load(%12) node_offset:17:16 to :17:19
%35 = break_inline(%11, %34)
For full context, use the command
zig ast-check -t main.zig
in main.zig: main.zig:numbers
> %11 = block_inline({%12..%35}) node_offset:11:17 to :11:23
in main.zig: main.zig:test.comptime
> %41 = decl_val("numbers") token_offset:21:9 to :21:16
in main.zig: main.zig:test.comptime
> %38 = block({%39..%45}) node_offset:20:17 to :20:18
/home/david/zig/src/value.zig:2564:29: 0x101f68e in hash (zig)
else => unreachable,
^
/home/david/zig/src/value.zig:2551:34: 0x10200f4 in hash (zig)
elem_val.hash(elem_ty, hasher, mod);
^
/home/david/zig/src/value.zig:2561:43: 0x101f637 in hash (zig)
field_val.hash(field_ty, hasher, mod);
^
/home/david/zig/src/TypedValue.zig:41:26: 0xd91a97 in hash (zig)
return tv.val.hash(tv.ty, hasher, mod);
^
/home/david/zig/src/Module.zig:288:34: 0xd9190e in hash (zig)
arg.hash(&hasher, ctx.module);
^
/home/david/zig/lib/std/hash_map.zig:1139:34: 0x1cf747d in getAdapted__anon_335127 (zig)
const hash = ctx.hash(key);
^
/home/david/zig/lib/std/hash_map.zig:1260:35: 0x19e2042 in getContext (zig)
return self.getAdapted(key, ctx);
^
/home/david/zig/src/Sema.zig:6753:50: 0x15ff403 in analyzeCall (zig)
if (mod.memoized_calls.getContext(memoized_call_key, .{ .module = mod })) |result| {
^
/home/david/zig/src/Sema.zig:6314:32: 0x10c3ed8 in zirCall (zig)
return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src);
^
/home/david/zig/src/Sema.zig:913:62: 0xe1501a in analyzeBodyInner (zig)
.call => try sema.zirCall(block, inst),
^
/home/david/zig/src/Sema.zig:812:45: 0xbdc895 in analyzeBodyBreak (zig)
const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
^
/home/david/zig/src/Sema.zig:1509:55: 0xe27108 in analyzeBodyInner (zig)
break :b try sema.analyzeBodyBreak(&child_block, inline_body);
^
/home/david/zig/src/Sema.zig:812:45: 0xbdc895 in analyzeBodyBreak (zig)
const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
^
/home/david/zig/src/Module.zig:4597:50: 0xbd8dae in semaDecl (zig)
const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
^
/home/david/zig/src/Module.zig:4209:38: 0x9cd406 in ensureDeclAnalyzed (zig)
const type_changed = mod.semaDecl(decl_index) catch |err| switch (err) {
^
/home/david/zig/src/Sema.zig:28218:32: 0x1641cf5 in ensureDeclAnalyzed (zig)
sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
^
/home/david/zig/src/Sema.zig:28260:32: 0x11b6021 in analyzeDeclRef (zig)
try sema.ensureDeclAnalyzed(decl_index);
^
/home/david/zig/src/Sema.zig:28188:45: 0x160aca4 in analyzeDeclVal (zig)
const decl_ref = try sema.analyzeDeclRef(decl_index);
^
/home/david/zig/src/Sema.zig:5900:31: 0x10c8827 in zirDeclVal (zig)
return sema.analyzeDeclVal(block, src, decl);
^
/home/david/zig/src/Sema.zig:923:65: 0xe15a25 in analyzeBodyInner (zig)
.decl_val => try sema.zirDeclVal(block, inst),
^
/home/david/zig/src/Sema.zig:5421:34: 0x163c0a9 in resolveBlockBody (zig)
if (sema.analyzeBodyInner(child_block, body)) |_| {
^
/home/david/zig/src/Sema.zig:5404:85: 0x1188d02 in zirBlock (zig)
return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
^
/home/david/zig/src/Sema.zig:1454:69: 0xe2637a in analyzeBodyInner (zig)
if (!block.is_comptime) break :blk try sema.zirBlock(block, inst);
^
/home/david/zig/src/Sema.zig:795:30: 0x106c698 in analyzeBody (zig)
_ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
^
/home/david/zig/src/Module.zig:5597:43: 0xdf731c in analyzeFnBody (zig)
sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
^
/home/david/zig/src/Module.zig:4295:40: 0xbbc421 in ensureFuncBodyAnalyzed (zig)
var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
^
/home/david/zig/src/Compilation.zig:3296:42: 0xbba4f5 in processOneJob (zig)
module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
^
/home/david/zig/src/Compilation.zig:3234:30: 0xa10e77 in performAllTheWork (zig)
try processOneJob(comp, work_item);
^
/home/david/zig/src/Compilation.zig:2425:31: 0xa0c9ee in update (zig)
try comp.performAllTheWork(main_progress_node);
^
/home/david/zig/src/main.zig:3641:20: 0xa3494d in updateModule (zig)
try comp.update();
^
/home/david/zig/src/main.zig:3278:17: 0x91a780 in buildOutputType (zig)
updateModule(gpa, comp, hook) catch |err| switch (err) {
^
/home/david/zig/src/main.zig:269:31: 0x8ee334 in mainArgs (zig)
return buildOutputType(gpa, arena, args, .zig_test);
^
/home/david/zig/src/main.zig:207:20: 0x8ed7e5 in main (zig)
return mainArgs(gpa, arena, args);
^
/home/david/zig/lib/std/start.zig:617:37: 0x8efecc in main (zig)
const result = root.main() catch |err| {
^
Expected Behavior
The compiler should not reach unreachable code.
Fixed by:
--- a/src/value.zig
+++ b/src/value.zig
@@ -2553,7 +2553,7 @@ pub const Value = extern union {
},
.Struct => {
switch (val.tag()) {
- .empty_struct_value => {},
+ .empty_struct_value, .the_only_possible_value => {},
.aggregate => {
const field_values = val.castTag(.aggregate).?.data;
for (field_values, 0..) |field_val, i| {
No longer repros because InternPool.