Repository navigation
Discarding first inline else capture on tagged union switch when using labeled switch fails to compile #26038
Description
Activity
- addedbugObserved behavior contradicts documented or intended behaviorObserved behavior contradicts documented or intended behavior
on Nov 24, 2025 Same issue as #24789
This is indeed a compiler bug your code should be valid :)
error trace etc
Compiler crash context: Analyzing 'build/repro.zig' %15 = dbg_stmt(2, 5) %16 = block_comptime(reason=type, { %17 = decl_val("TaggedUnion") token_offset:307:15 to :307:26 %18 = break_inline(%16, %17) }) node_offset:307:15 to :307:26 %19 = alloc(%16) node_offset:307:5 to :307:41 %20 = opt_eu_base_ptr_init(%19) node_offset:307:29 to :307:41 %21 = struct_init_field_ptr(%20, "a") node_offset:307:37 to :307:39 %22 = int(15) %23 = store_node(%21, %22) node_offset:307:37 to :307:39 %24 = validate_ptr_struct_init({ %21 = struct_init_field_ptr(%20, "a") node_offset:307:37 to :307:39 }) node_offset:307:29 to :307:41 %25 = make_ptr_const(%19) node_offset:307:5 to :307:41 %26 = dbg_var_ptr(%25, "tu") %27 = dbg_stmt(3, 5) %28 = load(%25) node_offset:308:17 to :308:19 %29 = typeof(%28) node_offset:308:17 to :308:19 %30 = dbg_stmt(3, 17) > %31 = switch_block(%28, tag_capture=%32, by_val inline else => { %33 = dbg_var_val(%32, "tag") %34 = dbg_stmt(7, 13) %35 = ensure_result_non_error(%32) node_offset:312:17 to :312:20 %36 = dbg_stmt(8, 13) %37 = validate_struct_init_result_ty(%29) node_offset:313:26 to :313:40 %38 = struct_init_field_type(%29, a) node_offset:313:34 to :313:38 %39 = struct_init([%38, %14]) node_offset:313:26 to :313:40 %40 = restore_err_ret_index_unconditional(%31) node_offset:313:13 to :313:40 %41 = switch_continue(%31, %39) %42 = break(%31, @void_value) }) node_offset:308:5 to :308:7 %43 = ensure_result_used(%31) node_offset:308:5 to :308:7 %44 = restore_err_ret_index_unconditional(.none) node_offset:306:1 to :306:10 %45 = ret_implicit(@void_value) token_offset:316:1 to :316:1 For full context, use the command build/stage4-master/bin/zig ast-check -t build/repro.zig thread 278351901 panic: reached unreachable code zig/src/InternPool.zig:10549:17: 0x107212a4b in childType (zig) else => unreachable, ^ zig/src/Type.zig:1946:42: 0x106fa09e7 in childTypeIp (zig) return Type.fromInterned(ip.childType(ty.toIntern())); ^ zig/src/Type.zig:1942:23: 0x106e7e4e3 in childType (zig) return childTypeIp(ty, &zcu.intern_pool); ^ zig/src/Sema.zig:10586:55: 0x108b0b803 in analyzeTagCapture (zig) const alloc_child = alloc_ty.childType(zcu); ^ zig/src/Sema.zig:10540:54: 0x108b12a33 in analyzeProngRuntime (zig) const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture); [...]😔 i swear i searched for an existing issue, dangit
well good to know i'm not crazy!
Can you please explain the compiler's error trace? I'm not familiar enough with the language to understand it. I'm new to the zig world,, hello!
Sure!
First off, to even get this kind of error trace you need a debug build of the compiler, further details are in the build from source section of the README.Everything below 'Compiler crash context:' (all of the
%stuff) is ZIR (zig intermediate representation), it's basically zig code that's already been parsed and turned into a series of instructions, but has not yet been semantically analyzed (basically type resolution/checking and everything comptime related). For more details on how ZIR works and how it's semantically analyzed have a look at this great post by mlugg.The little
>points out where something went wrong. In this case it's pointed at aswitch_blockinstruction, so the compiler crashed while trying to semantically analyze a switch statement.From there on it's just a normal stack trace. I shortened it a bit because the rest isn't really relevant, the issue is already known anyway. What's happening is that
analyzeTagCaptureis accessing anundefinedvalue (see #24792 for more details):fn analyzeTagCapture( spa: SwitchProngAnalysis, block: *Block, capture_src: LazySrcLoc, inline_case_capture: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { const sema = spa.sema; const pt = sema.pt; const zcu = pt.zcu; const operand_ty = switch (spa.operand) { .simple => |s| sema.typeOf(s.by_val), .loop => |l| ty: { const alloc_ty = sema.typeOf(l.operand_alloc); // <- operand_alloc is undefined at this point, so alloc_ty is also a garbage value const alloc_child = alloc_ty.childType(zcu); // <- the crash happens here, might as well have happened a line earlier if (l.operand_is_ref) break :ty alloc_child.childType(zcu); break :ty alloc_child; }, }; [...]
Welcome to the zig world! If you have more questions I'd recommend joining a zig community
Reacted by Rohlem
Zig Version
0.15.2
Steps to Reproduce and Observed Behavior
Sorry for the bulky issue name :< The bug is just kinda that specific.
The code above will fail to compile with no sensible error (only returning error code 1), despite the fact that this should be valid as far as I can tell.
Replacing
inline else => |_, tag|withinline else => |value, tag|and then discardingvalueusing_ = valuecompiles just fine.I don't personally see any use cases where this would actually matter, but it definitely shouldn't just crash without explanation like it does.
Expected Behavior
The given code should compile, or if it's not valid for some reason, it should return an actual compiler error instead of simply crashing the compiler.