From 72fa8d4880613d88f9f29dd78012b7253729bac7 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 23 Nov 2022 15:02:40 +0200 Subject: [PATCH 1/6] Sema: fix overflow arithmetic with runtime vectors It should return a a vector of bools for compatibility with scalar operands and stage1 until #10248 can be implemented. Closes #13201 --- src/Sema.zig | 10 +------- test/behavior/vector.zig | 49 +++++++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 0a28d2d7d0..24faa946c4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13468,7 +13468,6 @@ fn zirOverflowArithmetic( const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs); const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); - const ov_ty = tuple_ty.tupleFields().types[1]; // TODO: Remove and use `ov_ty` instead. // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`. const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.bool) else Type.bool; @@ -13619,14 +13618,7 @@ fn zirOverflowArithmetic( try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty); - const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero; - const zero_ov = try sema.addConstant(ov_ty, zero_ov_val); - - const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector) - block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty)) - else - block.addBinOp(.cmp_neq, overflow_bit, zero_ov); - return overflowed_inst; + return block.addBitCast(overflowed_ty, overflow_bit); }; try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index e756264418..30dd563abe 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -977,27 +977,35 @@ test "@addWithOverflow" { fn doTheTest() !void { { var result: @Vector(4, u8) = undefined; - var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result); + var lhs = @Vector(4, u8){ 250, 250, 250, 250 }; + var rhs = @Vector(4, u8){ 0, 5, 6, 10 }; + var overflow = @addWithOverflow(@Vector(4, u8), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, false, true, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } { var result: @Vector(4, i8) = undefined; - var overflow = @addWithOverflow(@Vector(4, i8), @Vector(4, i8){ -125, -125, 125, 125 }, @Vector(4, i8){ -3, -4, 2, 3 }, &result); + var lhs = @Vector(4, i8){ -125, -125, 125, 125 }; + var rhs = @Vector(4, i8){ -3, -4, 2, 3 }; + var overflow = @addWithOverflow(@Vector(4, i8), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, true, false, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } { var result: @Vector(4, u1) = undefined; - var overflow = @addWithOverflow(@Vector(4, u1), @Vector(4, u1){ 0, 0, 1, 1 }, @Vector(4, u1){ 0, 1, 0, 1 }, &result); + var lhs = @Vector(4, u1){ 0, 0, 1, 1 }; + var rhs = @Vector(4, u1){ 0, 1, 0, 1 }; + var overflow = @addWithOverflow(@Vector(4, u1), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, false, false, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } { var result: @Vector(4, u0) = undefined; - var overflow = @addWithOverflow(@Vector(4, u0), @Vector(4, u0){ 0, 0, 0, 0 }, @Vector(4, u0){ 0, 0, 0, 0 }, &result); + var lhs = @Vector(4, u0){ 0, 0, 0, 0 }; + var rhs = @Vector(4, u0){ 0, 0, 0, 0 }; + var overflow = @addWithOverflow(@Vector(4, u0), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, false, false, false }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } } }; @@ -1019,15 +1027,19 @@ test "@subWithOverflow" { fn doTheTest() !void { { var result: @Vector(2, u8) = undefined; - var overflow = @subWithOverflow(@Vector(2, u8), @Vector(2, u8){ 5, 5 }, @Vector(2, u8){ 5, 6 }, &result); + var lhs = @Vector(2, u8){ 5, 5 }; + var rhs = @Vector(2, u8){ 5, 6 }; + var overflow = @subWithOverflow(@Vector(2, u8), lhs, rhs, &result); var expected: @Vector(2, bool) = .{ false, true }; - try expect(mem.eql(bool, &@as([2]bool, overflow), &@as([2]bool, expected))); + try expectEqual(expected, overflow); } { var result: @Vector(4, i8) = undefined; - var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result); + var lhs = @Vector(4, i8){ -120, -120, 120, 120 }; + var rhs = @Vector(4, i8){ 8, 9, -7, -8 }; + var overflow = @subWithOverflow(@Vector(4, i8), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, true, false, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } } }; @@ -1048,9 +1060,11 @@ test "@mulWithOverflow" { const S = struct { fn doTheTest() !void { var result: @Vector(4, u8) = undefined; - var overflow = @mulWithOverflow(@Vector(4, u8), @Vector(4, u8){ 10, 10, 10, 10 }, @Vector(4, u8){ 25, 26, 0, 30 }, &result); + var lhs = @Vector(4, u8){ 10, 10, 10, 10 }; + var rhs = @Vector(4, u8){ 25, 26, 0, 30 }; + var overflow = @mulWithOverflow(@Vector(4, u8), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, true, false, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } }; try S.doTheTest(); @@ -1062,6 +1076,7 @@ test "@shlWithOverflow" { // stage1 doesn't support vector args return error.SkipZigTest; } + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -1070,9 +1085,11 @@ test "@shlWithOverflow" { const S = struct { fn doTheTest() !void { var result: @Vector(4, u8) = undefined; - var overflow = @shlWithOverflow(@Vector(4, u8), @Vector(4, u8){ 0, 1, 8, 255 }, @Vector(4, u3){ 7, 7, 7, 7 }, &result); + var lhs = @Vector(4, u8){ 0, 1, 8, 255 }; + var rhs = @Vector(4, u3){ 7, 7, 7, 7 }; + var overflow = @shlWithOverflow(@Vector(4, u8), lhs, rhs, &result); var expected: @Vector(4, bool) = .{ false, false, true, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + try expectEqual(expected, overflow); } }; try S.doTheTest(); From d0dd0bda1c8773243f98ac1e073afc32c4ce4cf2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 23 Nov 2022 15:47:51 +0200 Subject: [PATCH 2/6] Sema: resolve requires_comptime as part of resolveTypeLayout This was already partially done for structs but it needs to be done for Unions too and more thoroughly. Closes #13609 --- src/Sema.zig | 207 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 193 insertions(+), 14 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 24faa946c4..d414bfc1ce 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29126,20 +29126,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { } struct_obj.status = .have_layout; - - // In case of querying the ABI alignment of this struct, we will ask - // for hasRuntimeBits() of each field, so we need "requires comptime" - // to be known already before this function returns. - for (struct_obj.fields.values()) |field, i| { - _ = sema.typeRequiresComptime(field.ty) catch |err| switch (err) { - error.AnalysisFail => { - const msg = sema.err orelse return err; - try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); - return err; - }, - else => return err, - }; - } + _ = try sema.resolveTypeRequiresComptime(resolved_ty); } // otherwise it's a tuple; no need to resolve anything } @@ -29303,6 +29290,198 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { }; } union_obj.status = .have_layout; + _ = try sema.resolveTypeRequiresComptime(resolved_ty); +} + +// In case of querying the ABI alignment of this struct, we will ask +// for hasRuntimeBits() of each field, so we need "requires comptime" +// to be known already before this function returns. +pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { + return switch (ty.tag()) { + .u1, + .u8, + .i8, + .u16, + .i16, + .u29, + .u32, + .i32, + .u64, + .i64, + .u128, + .i128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .f16, + .f32, + .f64, + .f80, + .f128, + .anyopaque, + .bool, + .void, + .anyerror, + .noreturn, + .@"anyframe", + .null, + .undefined, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_options, + .prefetch_options, + .export_options, + .extern_options, + .manyptr_u8, + .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, + .const_slice_u8, + .const_slice_u8_sentinel_0, + .anyerror_void_error_union, + .empty_struct_literal, + .empty_struct, + .error_set, + .error_set_single, + .error_set_inferred, + .error_set_merged, + .@"opaque", + .generic_poison, + .array_u8, + .array_u8_sentinel_0, + .int_signed, + .int_unsigned, + .enum_simple, + => false, + + .single_const_pointer_to_comptime_int, + .type, + .comptime_int, + .comptime_float, + .enum_literal, + .type_info, + // These are function bodies, not function pointers. + .fn_noreturn_no_args, + .fn_void_no_args, + .fn_naked_noreturn_no_args, + .fn_ccc_void_no_args, + .function, + => true, + + .var_args_param => unreachable, + .inferred_alloc_mut => unreachable, + .inferred_alloc_const => unreachable, + .bound_fn => unreachable, + + .array, + .array_sentinel, + .vector, + => return sema.resolveTypeRequiresComptime(ty.childType()), + + .pointer, + .single_const_pointer, + .single_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + .const_slice, + .mut_slice, + => { + const child_ty = ty.childType(); + if (child_ty.zigTypeTag() == .Fn) { + return child_ty.fnInfo().is_generic; + } else { + return sema.resolveTypeRequiresComptime(child_ty); + } + }, + + .optional, + .optional_single_mut_pointer, + .optional_single_const_pointer, + => { + var buf: Type.Payload.ElemType = undefined; + return sema.resolveTypeRequiresComptime(ty.optionalChild(&buf)); + }, + + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); + for (tuple.types) |field_ty, i| { + const have_comptime_val = tuple.values[i].tag() != .unreachable_value; + if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) { + return true; + } + } + return false; + }, + + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + switch (struct_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + var requires_comptime = false; + struct_obj.requires_comptime = .wip; + for (struct_obj.fields.values()) |field| { + if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; + } + if (requires_comptime) { + struct_obj.requires_comptime = .yes; + } else { + struct_obj.requires_comptime = .no; + } + return requires_comptime; + }, + } + }, + + .@"union", .union_safety_tagged, .union_tagged => { + const union_obj = ty.cast(Type.Payload.Union).?.data; + switch (union_obj.requires_comptime) { + .no, .wip => return false, + .yes => return true, + .unknown => { + var requires_comptime = false; + union_obj.requires_comptime = .wip; + for (union_obj.fields.values()) |field| { + if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; + } + if (requires_comptime) { + union_obj.requires_comptime = .yes; + } else { + union_obj.requires_comptime = .no; + } + return requires_comptime; + }, + } + }, + + .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()), + .anyframe_T => { + const child_ty = ty.castTag(.anyframe_T).?.data; + return sema.resolveTypeRequiresComptime(child_ty); + }, + .enum_numbered => { + const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; + return sema.resolveTypeRequiresComptime(tag_ty); + }, + .enum_full, .enum_nonexhaustive => { + const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; + return sema.resolveTypeRequiresComptime(tag_ty); + }, + }; } /// Returns `error.AnalysisFail` if any of the types (recursively) failed to From bf62cb453bb1e91cd0ed448de70bd4f076b81906 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Nov 2022 02:05:58 +0200 Subject: [PATCH 3/6] Sema: handle anytype parameter requiring comptime Closes #13645 --- src/Sema.zig | 19 +++++++++++------- .../anytype_param_requires_comptime.zig | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 test/cases/compile_errors/anytype_param_requires_comptime.zig diff --git a/src/Sema.zig b/src/Sema.zig index d414bfc1ce..65bdf7e1e0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7031,16 +7031,21 @@ fn instantiateGenericCall( } const arg = uncasted_args[arg_i]; if (is_comptime) { - if (try sema.resolveMaybeUndefVal(arg)) |arg_val| { - const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); - child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); - } else { - return sema.failWithNeededComptime(block, .unneeded, ""); - } + const arg_val = (try sema.resolveMaybeUndefVal(arg)).?; + const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); + child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else if (is_anytype) { const arg_ty = sema.typeOf(arg); if (try sema.typeRequiresComptime(arg_ty)) { - const arg_val = try sema.resolveConstValue(block, .unneeded, arg, ""); + const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src); + _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known"); + return error.AnalysisFail; + }, + else => |e| return e, + }; const child_arg = try child_sema.addConstant(arg_ty, arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { diff --git a/test/cases/compile_errors/anytype_param_requires_comptime.zig b/test/cases/compile_errors/anytype_param_requires_comptime.zig new file mode 100644 index 0000000000..3e2b32b408 --- /dev/null +++ b/test/cases/compile_errors/anytype_param_requires_comptime.zig @@ -0,0 +1,20 @@ +const S = struct { + fn foo(b: u32, c: anytype) void { + const C = struct { + c: @TypeOf(c), + b: u32, + }; + bar(C{ .c = c, .b = b }); + } + fn bar(_: anytype) void {} +}; +pub export fn entry() void { + S.foo(0, u32); +} + +// error +// backend=stage2 +// target=native +// +// :7:14: error: unable to resolve comptime value +// :7:14: note: argument to parameter with comptime-only type must be comptime-known From 587ef60a28ae7c1c780e02ecf60b331ead53bc7f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Nov 2022 02:40:06 +0200 Subject: [PATCH 4/6] Sema: resolve union fields before using getTagType Closes #13649 --- src/Sema.zig | 6 ++++-- ...ds_are_resolved_before_tag_type_is_needed.zig | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig diff --git a/src/Sema.zig b/src/Sema.zig index 65bdf7e1e0..d6a563bd36 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7580,7 +7580,8 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) { .Enum => operand, .Union => blk: { - const tag_ty = operand_ty.unionTagType() orelse { + const union_ty = try sema.resolveTypeFields(operand_ty); + const tag_ty = union_ty.unionTagType() orelse { return sema.fail( block, operand_src, @@ -22713,7 +22714,8 @@ fn fieldPtr( return inst; } } - if (child_type.unionTagType()) |enum_ty| { + const union_ty = try sema.resolveTypeFields(child_type); + if (union_ty.unionTagType()) |enum_ty| { if (enum_ty.enumFieldIndex(field_name)) |field_index| { const field_index_u32 = @intCast(u32, field_index); var anon_decl = try block.startAnonDecl(); diff --git a/test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig b/test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig new file mode 100644 index 0000000000..7ef4c80d5a --- /dev/null +++ b/test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig @@ -0,0 +1,16 @@ +const T = union(enum) { + a, + pub fn f(self: T) void { + _ = self; + } +}; +pub export fn entry() void { + T.a.f(); +} + +// error +// backend=stage2 +// target=native +// +// :8:8: error: no field or member function named 'f' in '@typeInfo(tmp.T).Union.tag_type.?' +// :1:11: note: enum declared here From fe388982462db067bdb697ec3e89c2c13ce2ffa8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Nov 2022 23:28:50 +0200 Subject: [PATCH 5/6] Sema: pass c_import_buf to child block in more places Closes #13651 --- src/AstGen.zig | 3 +++ src/Sema.zig | 2 ++ test/cases/compile_errors/cimport.zig | 15 +++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 test/cases/compile_errors/cimport.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index db80a2f350..945c64107c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7674,6 +7674,7 @@ fn typeOf( var typeof_scope = gz.makeSubBlock(scope); typeof_scope.force_comptime = false; + typeof_scope.c_import = false; defer typeof_scope.unstack(); const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node); @@ -8532,6 +8533,8 @@ fn cImport( const astgen = gz.astgen; const gpa = astgen.gpa; + if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{}); + var block_scope = gz.makeSubBlock(scope); block_scope.force_comptime = true; block_scope.c_import = true; diff --git a/src/Sema.zig b/src/Sema.zig index d6a563bd36..f056164f80 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5105,6 +5105,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro .is_typeof = parent_block.is_typeof, .want_safety = parent_block.want_safety, .float_mode = parent_block.float_mode, + .c_import_buf = parent_block.c_import_buf, .runtime_cond = parent_block.runtime_cond, .runtime_loop = parent_block.runtime_loop, .runtime_index = parent_block.runtime_index, @@ -10266,6 +10267,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .comptime_reason = block.comptime_reason, .is_typeof = block.is_typeof, .switch_else_err_ty = else_error_ty, + .c_import_buf = block.c_import_buf, .runtime_cond = block.runtime_cond, .runtime_loop = block.runtime_loop, .runtime_index = block.runtime_index, diff --git a/test/cases/compile_errors/cimport.zig b/test/cases/compile_errors/cimport.zig new file mode 100644 index 0000000000..855f9aeb40 --- /dev/null +++ b/test/cases/compile_errors/cimport.zig @@ -0,0 +1,15 @@ +const b = @cDefine("foo", "1"); +const c = @cImport({ + _ = @TypeOf(@cDefine("foo", "1")); +}); +const d = @cImport({ + _ = @cImport(@cDefine("foo", "1")); +}); + +// error +// backend=stage2 +// target=native +// +// :1:11: error: C define valid only inside C import block +// :3:17: error: C define valid only inside C import block +// :6:9: error: cannot nest @cImport From 71937f75d83d960e0ec61eb697e4e3420685f9ca Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 26 Nov 2022 18:04:07 +0200 Subject: [PATCH 6/6] Sema: correctly detect union target in `zirSwitchBlock` Closes #13655 --- src/Sema.zig | 4 +++- test/behavior/union.zig | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f056164f80..3476024222 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9698,10 +9698,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }; const maybe_union_ty = blk: { + const zir_tags = sema.code.instructions.items(.tag); const zir_data = sema.code.instructions.items(.data); const cond_index = Zir.refToIndex(extra.data.operand).?; const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable; - break :blk sema.typeOf(raw_operand); + const target_ty = sema.typeOf(raw_operand); + break :blk if (zir_tags[cond_index] == .switch_cond_ref) target_ty.elemType() else target_ty; }; const union_originally = maybe_union_ty.zigTypeTag() == .Union; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 37cd72e319..aaeb586cdf 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1287,7 +1287,14 @@ test "noreturn field in union" { try expect(a == .a); }, } - try expect(count == 5); + switch (a) { + .a => count += 1, + .b, .c => |*val| { + _ = val; + @compileError("bad"); + }, + } + try expect(count == 6); } test "union and enum field order doesn't match" {