authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-26 22:03:49+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-26 22:03:49+02:00
log304e828088f0b03d47f7b0b3df7b1449c0a1f2d6
treea048ea78944632c0d088e9316e468ad0af21340c
parentf61c5f3f5242fb3e96891c82eb3dd16fdb8a2c7b
parent71937f75d83d960e0ec61eb697e4e3420685f9ca
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13637 from Vexu/stage2-fixes

Stage2 bug fixes

7 files changed, 310 insertions(+), 50 deletions(-)

src/AstGen.zig+3
......@@ -7717,6 +7717,7 @@ fn typeOf(
77177717
77187718 var typeof_scope = gz.makeSubBlock(scope);
77197719 typeof_scope.force_comptime = false;
7720 typeof_scope.c_import = false;
77207721 defer typeof_scope.unstack();
77217722
77227723 const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
......@@ -8575,6 +8576,8 @@ fn cImport(
85758576 const astgen = gz.astgen;
85768577 const gpa = astgen.gpa;
85778578
8579 if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
8580
85788581 var block_scope = gz.makeSubBlock(scope);
85798582 block_scope.force_comptime = true;
85808583 block_scope.c_import = true;
src/Sema.zig+215-33
......@@ -5105,6 +5105,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro
51055105 .is_typeof = parent_block.is_typeof,
51065106 .want_safety = parent_block.want_safety,
51075107 .float_mode = parent_block.float_mode,
5108 .c_import_buf = parent_block.c_import_buf,
51085109 .runtime_cond = parent_block.runtime_cond,
51095110 .runtime_loop = parent_block.runtime_loop,
51105111 .runtime_index = parent_block.runtime_index,
......@@ -7031,16 +7032,21 @@ fn instantiateGenericCall(
70317032 }
70327033 const arg = uncasted_args[arg_i];
70337034 if (is_comptime) {
7034 if (try sema.resolveMaybeUndefVal(arg)) |arg_val| {
7035 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
7036 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
7037 } else {
7038 return sema.failWithNeededComptime(block, .unneeded, "");
7039 }
7035 const arg_val = (try sema.resolveMaybeUndefVal(arg)).?;
7036 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
7037 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
70407038 } else if (is_anytype) {
70417039 const arg_ty = sema.typeOf(arg);
70427040 if (try sema.typeRequiresComptime(arg_ty)) {
7043 const arg_val = try sema.resolveConstValue(block, .unneeded, arg, "");
7041 const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) {
7042 error.NeededSourceLocation => {
7043 const decl = sema.mod.declPtr(block.src_decl);
7044 const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src);
7045 _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known");
7046 return error.AnalysisFail;
7047 },
7048 else => |e| return e,
7049 };
70447050 const child_arg = try child_sema.addConstant(arg_ty, arg_val);
70457051 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
70467052 } else {
......@@ -7575,7 +7581,8 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
75757581 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) {
75767582 .Enum => operand,
75777583 .Union => blk: {
7578 const tag_ty = operand_ty.unionTagType() orelse {
7584 const union_ty = try sema.resolveTypeFields(operand_ty);
7585 const tag_ty = union_ty.unionTagType() orelse {
75797586 return sema.fail(
75807587 block,
75817588 operand_src,
......@@ -9691,10 +9698,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
96919698 };
96929699
96939700 const maybe_union_ty = blk: {
9701 const zir_tags = sema.code.instructions.items(.tag);
96949702 const zir_data = sema.code.instructions.items(.data);
96959703 const cond_index = Zir.refToIndex(extra.data.operand).?;
96969704 const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable;
9697 break :blk sema.typeOf(raw_operand);
9705 const target_ty = sema.typeOf(raw_operand);
9706 break :blk if (zir_tags[cond_index] == .switch_cond_ref) target_ty.elemType() else target_ty;
96989707 };
96999708 const union_originally = maybe_union_ty.zigTypeTag() == .Union;
97009709
......@@ -10260,6 +10269,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1026010269 .comptime_reason = block.comptime_reason,
1026110270 .is_typeof = block.is_typeof,
1026210271 .switch_else_err_ty = else_error_ty,
10272 .c_import_buf = block.c_import_buf,
1026310273 .runtime_cond = block.runtime_cond,
1026410274 .runtime_loop = block.runtime_loop,
1026510275 .runtime_index = block.runtime_index,
......@@ -13479,7 +13489,6 @@ fn zirOverflowArithmetic(
1347913489 const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs);
1348013490
1348113491 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
13482 const ov_ty = tuple_ty.tupleFields().types[1];
1348313492 // TODO: Remove and use `ov_ty` instead.
1348413493 // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
1348513494 const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.bool) else Type.bool;
......@@ -13630,14 +13639,7 @@ fn zirOverflowArithmetic(
1363013639 try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
1363113640
1363213641 const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
13633 const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
13634 const zero_ov = try sema.addConstant(ov_ty, zero_ov_val);
13635
13636 const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector)
13637 block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty))
13638 else
13639 block.addBinOp(.cmp_neq, overflow_bit, zero_ov);
13640 return overflowed_inst;
13642 return block.addBitCast(overflowed_ty, overflow_bit);
1364113643 };
1364213644
1364313645 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
......@@ -22675,7 +22677,8 @@ fn fieldPtr(
2267522677 return inst;
2267622678 }
2267722679 }
22678 if (child_type.unionTagType()) |enum_ty| {
22680 const union_ty = try sema.resolveTypeFields(child_type);
22681 if (union_ty.unionTagType()) |enum_ty| {
2267922682 if (enum_ty.enumFieldIndex(field_name)) |field_index| {
2268022683 const field_index_u32 = @intCast(u32, field_index);
2268122684 var anon_decl = try block.startAnonDecl();
......@@ -29101,20 +29104,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
2910129104 }
2910229105
2910329106 struct_obj.status = .have_layout;
29104
29105 // In case of querying the ABI alignment of this struct, we will ask
29106 // for hasRuntimeBits() of each field, so we need "requires comptime"
29107 // to be known already before this function returns.
29108 for (struct_obj.fields.values()) |field, i| {
29109 _ = sema.typeRequiresComptime(field.ty) catch |err| switch (err) {
29110 error.AnalysisFail => {
29111 const msg = sema.err orelse return err;
29112 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
29113 return err;
29114 },
29115 else => return err,
29116 };
29117 }
29107 _ = try sema.resolveTypeRequiresComptime(resolved_ty);
2911829108 }
2911929109 // otherwise it's a tuple; no need to resolve anything
2912029110}
......@@ -29278,6 +29268,198 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
2927829268 };
2927929269 }
2928029270 union_obj.status = .have_layout;
29271 _ = try sema.resolveTypeRequiresComptime(resolved_ty);
29272}
29273
29274// In case of querying the ABI alignment of this struct, we will ask
29275// for hasRuntimeBits() of each field, so we need "requires comptime"
29276// to be known already before this function returns.
29277pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
29278 return switch (ty.tag()) {
29279 .u1,
29280 .u8,
29281 .i8,
29282 .u16,
29283 .i16,
29284 .u29,
29285 .u32,
29286 .i32,
29287 .u64,
29288 .i64,
29289 .u128,
29290 .i128,
29291 .usize,
29292 .isize,
29293 .c_short,
29294 .c_ushort,
29295 .c_int,
29296 .c_uint,
29297 .c_long,
29298 .c_ulong,
29299 .c_longlong,
29300 .c_ulonglong,
29301 .c_longdouble,
29302 .f16,
29303 .f32,
29304 .f64,
29305 .f80,
29306 .f128,
29307 .anyopaque,
29308 .bool,
29309 .void,
29310 .anyerror,
29311 .noreturn,
29312 .@"anyframe",
29313 .null,
29314 .undefined,
29315 .atomic_order,
29316 .atomic_rmw_op,
29317 .calling_convention,
29318 .address_space,
29319 .float_mode,
29320 .reduce_op,
29321 .call_options,
29322 .prefetch_options,
29323 .export_options,
29324 .extern_options,
29325 .manyptr_u8,
29326 .manyptr_const_u8,
29327 .manyptr_const_u8_sentinel_0,
29328 .const_slice_u8,
29329 .const_slice_u8_sentinel_0,
29330 .anyerror_void_error_union,
29331 .empty_struct_literal,
29332 .empty_struct,
29333 .error_set,
29334 .error_set_single,
29335 .error_set_inferred,
29336 .error_set_merged,
29337 .@"opaque",
29338 .generic_poison,
29339 .array_u8,
29340 .array_u8_sentinel_0,
29341 .int_signed,
29342 .int_unsigned,
29343 .enum_simple,
29344 => false,
29345
29346 .single_const_pointer_to_comptime_int,
29347 .type,
29348 .comptime_int,
29349 .comptime_float,
29350 .enum_literal,
29351 .type_info,
29352 // These are function bodies, not function pointers.
29353 .fn_noreturn_no_args,
29354 .fn_void_no_args,
29355 .fn_naked_noreturn_no_args,
29356 .fn_ccc_void_no_args,
29357 .function,
29358 => true,
29359
29360 .var_args_param => unreachable,
29361 .inferred_alloc_mut => unreachable,
29362 .inferred_alloc_const => unreachable,
29363 .bound_fn => unreachable,
29364
29365 .array,
29366 .array_sentinel,
29367 .vector,
29368 => return sema.resolveTypeRequiresComptime(ty.childType()),
29369
29370 .pointer,
29371 .single_const_pointer,
29372 .single_mut_pointer,
29373 .many_const_pointer,
29374 .many_mut_pointer,
29375 .c_const_pointer,
29376 .c_mut_pointer,
29377 .const_slice,
29378 .mut_slice,
29379 => {
29380 const child_ty = ty.childType();
29381 if (child_ty.zigTypeTag() == .Fn) {
29382 return child_ty.fnInfo().is_generic;
29383 } else {
29384 return sema.resolveTypeRequiresComptime(child_ty);
29385 }
29386 },
29387
29388 .optional,
29389 .optional_single_mut_pointer,
29390 .optional_single_const_pointer,
29391 => {
29392 var buf: Type.Payload.ElemType = undefined;
29393 return sema.resolveTypeRequiresComptime(ty.optionalChild(&buf));
29394 },
29395
29396 .tuple, .anon_struct => {
29397 const tuple = ty.tupleFields();
29398 for (tuple.types) |field_ty, i| {
29399 const have_comptime_val = tuple.values[i].tag() != .unreachable_value;
29400 if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) {
29401 return true;
29402 }
29403 }
29404 return false;
29405 },
29406
29407 .@"struct" => {
29408 const struct_obj = ty.castTag(.@"struct").?.data;
29409 switch (struct_obj.requires_comptime) {
29410 .no, .wip => return false,
29411 .yes => return true,
29412 .unknown => {
29413 var requires_comptime = false;
29414 struct_obj.requires_comptime = .wip;
29415 for (struct_obj.fields.values()) |field| {
29416 if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
29417 }
29418 if (requires_comptime) {
29419 struct_obj.requires_comptime = .yes;
29420 } else {
29421 struct_obj.requires_comptime = .no;
29422 }
29423 return requires_comptime;
29424 },
29425 }
29426 },
29427
29428 .@"union", .union_safety_tagged, .union_tagged => {
29429 const union_obj = ty.cast(Type.Payload.Union).?.data;
29430 switch (union_obj.requires_comptime) {
29431 .no, .wip => return false,
29432 .yes => return true,
29433 .unknown => {
29434 var requires_comptime = false;
29435 union_obj.requires_comptime = .wip;
29436 for (union_obj.fields.values()) |field| {
29437 if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
29438 }
29439 if (requires_comptime) {
29440 union_obj.requires_comptime = .yes;
29441 } else {
29442 union_obj.requires_comptime = .no;
29443 }
29444 return requires_comptime;
29445 },
29446 }
29447 },
29448
29449 .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()),
29450 .anyframe_T => {
29451 const child_ty = ty.castTag(.anyframe_T).?.data;
29452 return sema.resolveTypeRequiresComptime(child_ty);
29453 },
29454 .enum_numbered => {
29455 const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
29456 return sema.resolveTypeRequiresComptime(tag_ty);
29457 },
29458 .enum_full, .enum_nonexhaustive => {
29459 const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
29460 return sema.resolveTypeRequiresComptime(tag_ty);
29461 },
29462 };
2928129463}
2928229464
2928329465/// Returns `error.AnalysisFail` if any of the types (recursively) failed to
test/behavior/union.zig+8-1
......@@ -1287,7 +1287,14 @@ test "noreturn field in union" {
12871287 try expect(a == .a);
12881288 },
12891289 }
1290 try expect(count == 5);
1290 switch (a) {
1291 .a => count += 1,
1292 .b, .c => |*val| {
1293 _ = val;
1294 @compileError("bad");
1295 },
1296 }
1297 try expect(count == 6);
12911298}
12921299
12931300test "union and enum field order doesn't match" {
test/behavior/vector.zig+33-16
......@@ -977,27 +977,35 @@ test "@addWithOverflow" {
977977 fn doTheTest() !void {
978978 {
979979 var result: @Vector(4, u8) = undefined;
980 var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result);
980 var lhs = @Vector(4, u8){ 250, 250, 250, 250 };
981 var rhs = @Vector(4, u8){ 0, 5, 6, 10 };
982 var overflow = @addWithOverflow(@Vector(4, u8), lhs, rhs, &result);
981983 var expected: @Vector(4, bool) = .{ false, false, true, true };
982 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
984 try expectEqual(expected, overflow);
983985 }
984986 {
985987 var result: @Vector(4, i8) = undefined;
986 var overflow = @addWithOverflow(@Vector(4, i8), @Vector(4, i8){ -125, -125, 125, 125 }, @Vector(4, i8){ -3, -4, 2, 3 }, &result);
988 var lhs = @Vector(4, i8){ -125, -125, 125, 125 };
989 var rhs = @Vector(4, i8){ -3, -4, 2, 3 };
990 var overflow = @addWithOverflow(@Vector(4, i8), lhs, rhs, &result);
987991 var expected: @Vector(4, bool) = .{ false, true, false, true };
988 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
992 try expectEqual(expected, overflow);
989993 }
990994 {
991995 var result: @Vector(4, u1) = undefined;
992 var overflow = @addWithOverflow(@Vector(4, u1), @Vector(4, u1){ 0, 0, 1, 1 }, @Vector(4, u1){ 0, 1, 0, 1 }, &result);
996 var lhs = @Vector(4, u1){ 0, 0, 1, 1 };
997 var rhs = @Vector(4, u1){ 0, 1, 0, 1 };
998 var overflow = @addWithOverflow(@Vector(4, u1), lhs, rhs, &result);
993999 var expected: @Vector(4, bool) = .{ false, false, false, true };
994 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
1000 try expectEqual(expected, overflow);
9951001 }
9961002 {
9971003 var result: @Vector(4, u0) = undefined;
998 var overflow = @addWithOverflow(@Vector(4, u0), @Vector(4, u0){ 0, 0, 0, 0 }, @Vector(4, u0){ 0, 0, 0, 0 }, &result);
1004 var lhs = @Vector(4, u0){ 0, 0, 0, 0 };
1005 var rhs = @Vector(4, u0){ 0, 0, 0, 0 };
1006 var overflow = @addWithOverflow(@Vector(4, u0), lhs, rhs, &result);
9991007 var expected: @Vector(4, bool) = .{ false, false, false, false };
1000 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
1008 try expectEqual(expected, overflow);
10011009 }
10021010 }
10031011 };
......@@ -1019,15 +1027,19 @@ test "@subWithOverflow" {
10191027 fn doTheTest() !void {
10201028 {
10211029 var result: @Vector(2, u8) = undefined;
1022 var overflow = @subWithOverflow(@Vector(2, u8), @Vector(2, u8){ 5, 5 }, @Vector(2, u8){ 5, 6 }, &result);
1030 var lhs = @Vector(2, u8){ 5, 5 };
1031 var rhs = @Vector(2, u8){ 5, 6 };
1032 var overflow = @subWithOverflow(@Vector(2, u8), lhs, rhs, &result);
10231033 var expected: @Vector(2, bool) = .{ false, true };
1024 try expect(mem.eql(bool, &@as([2]bool, overflow), &@as([2]bool, expected)));
1034 try expectEqual(expected, overflow);
10251035 }
10261036 {
10271037 var result: @Vector(4, i8) = undefined;
1028 var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result);
1038 var lhs = @Vector(4, i8){ -120, -120, 120, 120 };
1039 var rhs = @Vector(4, i8){ 8, 9, -7, -8 };
1040 var overflow = @subWithOverflow(@Vector(4, i8), lhs, rhs, &result);
10291041 var expected: @Vector(4, bool) = .{ false, true, false, true };
1030 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
1042 try expectEqual(expected, overflow);
10311043 }
10321044 }
10331045 };
......@@ -1048,9 +1060,11 @@ test "@mulWithOverflow" {
10481060 const S = struct {
10491061 fn doTheTest() !void {
10501062 var result: @Vector(4, u8) = undefined;
1051 var overflow = @mulWithOverflow(@Vector(4, u8), @Vector(4, u8){ 10, 10, 10, 10 }, @Vector(4, u8){ 25, 26, 0, 30 }, &result);
1063 var lhs = @Vector(4, u8){ 10, 10, 10, 10 };
1064 var rhs = @Vector(4, u8){ 25, 26, 0, 30 };
1065 var overflow = @mulWithOverflow(@Vector(4, u8), lhs, rhs, &result);
10521066 var expected: @Vector(4, bool) = .{ false, true, false, true };
1053 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
1067 try expectEqual(expected, overflow);
10541068 }
10551069 };
10561070 try S.doTheTest();
......@@ -1062,6 +1076,7 @@ test "@shlWithOverflow" {
10621076 // stage1 doesn't support vector args
10631077 return error.SkipZigTest;
10641078 }
1079 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10651080 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10661081 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10671082 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -1070,9 +1085,11 @@ test "@shlWithOverflow" {
10701085 const S = struct {
10711086 fn doTheTest() !void {
10721087 var result: @Vector(4, u8) = undefined;
1073 var overflow = @shlWithOverflow(@Vector(4, u8), @Vector(4, u8){ 0, 1, 8, 255 }, @Vector(4, u3){ 7, 7, 7, 7 }, &result);
1088 var lhs = @Vector(4, u8){ 0, 1, 8, 255 };
1089 var rhs = @Vector(4, u3){ 7, 7, 7, 7 };
1090 var overflow = @shlWithOverflow(@Vector(4, u8), lhs, rhs, &result);
10741091 var expected: @Vector(4, bool) = .{ false, false, true, true };
1075 try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
1092 try expectEqual(expected, overflow);
10761093 }
10771094 };
10781095 try S.doTheTest();
test/cases/compile_errors/anytype_param_requires_comptime.zig created+20
......@@ -0,0 +1,20 @@
1const S = struct {
2 fn foo(b: u32, c: anytype) void {
3 const C = struct {
4 c: @TypeOf(c),
5 b: u32,
6 };
7 bar(C{ .c = c, .b = b });
8 }
9 fn bar(_: anytype) void {}
10};
11pub export fn entry() void {
12 S.foo(0, u32);
13}
14
15// error
16// backend=stage2
17// target=native
18//
19// :7:14: error: unable to resolve comptime value
20// :7:14: note: argument to parameter with comptime-only type must be comptime-known
test/cases/compile_errors/cimport.zig created+15
......@@ -0,0 +1,15 @@
1const b = @cDefine("foo", "1");
2const c = @cImport({
3 _ = @TypeOf(@cDefine("foo", "1"));
4});
5const d = @cImport({
6 _ = @cImport(@cDefine("foo", "1"));
7});
8
9// error
10// backend=stage2
11// target=native
12//
13// :1:11: error: C define valid only inside C import block
14// :3:17: error: C define valid only inside C import block
15// :6:9: error: cannot nest @cImport
test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig created+16
......@@ -0,0 +1,16 @@
1const T = union(enum) {
2 a,
3 pub fn f(self: T) void {
4 _ = self;
5 }
6};
7pub export fn entry() void {
8 T.a.f();
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :8:8: error: no field or member function named 'f' in '@typeInfo(tmp.T).Union.tag_type.?'
16// :1:11: note: enum declared here