authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-07-17 23:20:09+02:00
committergravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-07-17 23:26:17+02:00
log115e0894dbd39e8baa15f9b883105697a8af201b
tree1e4e9d2a82a4de4ce86ed5e4d28b23b37c129017
parent58a94eaae24e955d4a4a69238617e1bdd90270e7
parentb203181a8a12c3cef0d9e11269c777bf677cb5aa

Merge pull request 'compiler: add `@backingInt` and `@fromBackingInt`' (#35966) from justusk/backing-int into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35966

74 files changed, 1824 insertions(+), 367 deletions(-)

doc/langref.html.in+62-14
......@@ -2271,6 +2271,7 @@ or
22712271 This even works at {#link|comptime#}:
22722272 </p>
22732273 {#code|test_packed_structs.zig#}
2274
22742275 <p>
22752276 The backing integer can be inferred or explicitly provided. When
22762277 inferred, it will be unsigned. When explicitly provided, its bit width
......@@ -2279,6 +2280,12 @@ or
22792280 </p>
22802281 {#code|test_missized_packed_struct.zig#}
22812282
2283 <p>
2284 A {#syntax#}packed struct{#endsyntax#} can be converted to and from its backing
2285 integer using {#link|@backingInt#} and {#link|@fromBackingInt#}:
2286 </p>
2287 {#code|test_packed_struct_backing_int.zig#}
2288
22822289 <p>
22832290 Zig allows the address to be taken of a non-byte-aligned field:
22842291 </p>
......@@ -2401,7 +2408,7 @@ or
24012408 {#header_open|enum#}
24022409 {#code|test_enums.zig#}
24032410
2404 {#see_also|@typeInfo|@tagName|@sizeOf#}
2411 {#see_also|@backingInt|@fromBackingInt|@typeInfo|@tagName|@sizeOf|noreturn#}
24052412
24062413 {#header_open|extern enum#}
24072414 <p>
......@@ -2431,9 +2438,7 @@ or
24312438 The enum must specify a tag type and cannot consume every enumeration value.
24322439 </p>
24332440 <p>
2434 {#link|@enumFromInt#} on a non-exhaustive enum involves the safety semantics
2435 of {#link|@intCast#} to the integer tag type, but beyond that always results in
2436 a well-defined enum value.
2441 {#link|@fromBackingInt#} on a non-exhaustive enum always results in a valid enum value.
24372442 </p>
24382443 <p>
24392444 A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.
......@@ -2461,7 +2466,8 @@ or
24612466 {#code|test_simple_union.zig#}
24622467
24632468 <p>
2464 In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
2469 In order to use {#link|switch#} with a union, it must be a {#link|tagged union|Tagged union#}
2470 or a {#link|packed union#}.
24652471 </p>
24662472 <p>
24672473 To initialize a union when the tag is a {#link|comptime#}-known name, see {#link|@unionInit#}.
......@@ -2490,7 +2496,7 @@ or
24902496 <p>
24912497 Unions with inferred enum tag types can also assign ordinal values to their inferred tag.
24922498 This requires the tag to specify an explicit integer type.
2493 {#link|@intFromEnum#} can be used to access the ordinal value corresponding to the active field.
2499 {#link|@backingInt#} can be used to access the ordinal value corresponding to the active field.
24942500 </p>
24952501 {#code|test_tagged_union_with_tag_values.zig#}
24962502
......@@ -2521,6 +2527,7 @@ or
25212527 </p>
25222528 {#code|test_packed_union_equality.zig#}
25232529
2530 {#see_also|@backingInt|@fromBackingInt#}
25242531 {#header_close#}
25252532
25262533 {#header_open|Anonymous Union Literals#}
......@@ -3586,14 +3593,14 @@ void do_a_thing(struct Foo *foo) {
35863593 <ul>
35873594 <li>{#link|@bitCast#} - change type but maintain bit representation</li>
35883595 <li>{#link|@alignCast#} - make a pointer have more alignment</li>
3589 <li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>
3596 <li>{#link|@fromBackingInt#} - obtain an enum or a packed struct/union value based on its backing integer</li>
35903597 <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>
35913598 <li>{#link|@errorCast#} - convert to a smaller error set</li>
35923599 <li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
35933600 <li>{#link|@floatFromInt#} - convert an integer to a float value</li>
35943601 <li>{#link|@intCast#} - convert between integer types</li>
35953602 <li>{#link|@intFromBool#} - convert true to 1 and false to 0</li>
3596 <li>{#link|@intFromEnum#} - obtain the integer tag value of an enum or tagged union</li>
3603 <li>{#link|@backingInt#} - obtain the backing integer value of an enum or a packed struct/union</li>
35973604 <li>{#link|@intFromError#} - obtain the integer value of an error code</li>
35983605 <li>{#link|@round#}, {#link|@floor#}, {#link|@ceil#}, {#link|@trunc#} - float to integer conversion</li>
35993606 <li>{#link|@intFromPtr#} - obtain the address of a pointer</li>
......@@ -4432,6 +4439,18 @@ comptime {
44324439 {#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
44334440 {#header_close#}
44344441
4442 {#header_open|@backingInt#}
4443 <pre>{#syntax#}@backingInt(enum_or_bitpack: T) BackingInt(T){#endsyntax#}</pre>
4444 <p>
4445 Converts an {#link|enum#}, a {#link|packed struct#} or a {#link|packed union#} value
4446 to its backing integer.
4447 </p>
4448 <p>
4449 Also works with {#link|tagged unions|Tagged union#}, acting on the active enum tag value.
4450 </p>
4451 {#see_also|@fromBackingInt|@bitCast#}
4452 {#header_close#}
4453
44354454 {#header_open|@bitCast#}
44364455 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
44374456 <p>
......@@ -4452,8 +4471,18 @@ comptime {
44524471 <li>Convert {#syntax#}i32{#endsyntax#} to {#syntax#}u32{#endsyntax#} preserving twos complement</li>
44534472 </ul>
44544473 <p>
4455 Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time. It's a compile error to bitcast a value of undefined layout; this means that, besides the restriction from types which possess dedicated casting builtins (enums, pointers, error sets), bare structs, error unions, slices, optionals, and any other type without a well-defined memory layout, also cannot be used in this operation.
4474 Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time.
4475 It's a compile error to bitcast a value of undefined layout; this means that,
4476 besides the restriction from types which possess dedicated casting builtins
4477 (pointers, error sets), bare structs, error unions, slices, optionals, and any
4478 other type without a well-defined memory layout, also cannot be used in this
4479 operation.
4480 </p>
4481 <p>
4482 Attempting to convert an integer with no corresponding tag value to an
4483 {#syntax#}enum{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}.
44564484 </p>
4485 {#see_also|@ptrCast|@intFromPtr|@ptrFromInt|@errorCast|@intFromError|@errorFromInt|@backingInt|@fromBackingInt#}
44574486 {#header_close#}
44584487
44594488 {#header_open|@bitOffsetOf#}
......@@ -4798,6 +4827,9 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
47984827 {#header_open|@enumFromInt#}
47994828 <pre>{#syntax#}@enumFromInt(integer: anytype) anytype{#endsyntax#}</pre>
48004829 <p>
4830 Deprecated. Use {#link|@fromBackingInt#} or {#link|@bitCast#} instead.
4831 </p>
4832 <p>
48014833 Converts an integer into an {#link|enum#} value. The return type is the inferred result type.
48024834 </p>
48034835 <p>
......@@ -4953,6 +4985,22 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
49534985 </p>
49544986 {#header_close#}
49554987
4988 {#header_open|@fromBackingInt#}
4989 <pre>{#syntax#}@fromBackingInt(backing_int: BackingInt(T)) T{#endsyntax#}</pre>
4990 <p>
4991 Converts an integer into a {#link|enum#}, a {#link|packed struct#} or a
4992 {#link|packed union#} value. The return type is the inferred result type.
4993 </p>
4994 <p>
4995 Attempting to convert an integer with no corresponding tag value to an
4996 {#syntax#}enum{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}.
4997 Note that a {#link|non-exhaustive enum|Non-exhaustive enum#} has corresponding values for
4998 all integers in the enum's integer tag type: the {#syntax#}_{#endsyntax#} value represents
4999 all the remaining unnamed integers in the enum's tag type.
5000 </p>
5001 {#see_also|@backingInt|@bitCast#}
5002 {#header_close#}
5003
49565004 {#header_open|@hasDecl#}
49575005 <pre>{#syntax#}@hasDecl(comptime Namespace: type, comptime name: []const u8) bool{#endsyntax#}</pre>
49585006 <p>Returns whether or not a {#link|Namespace#} has a declaration matching {#syntax#}name{#endsyntax#}.</p>
......@@ -5035,12 +5083,11 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50355083 {#header_open|@intFromEnum#}
50365084 <pre>{#syntax#}@intFromEnum(enum_or_tagged_union: anytype) anytype{#endsyntax#}</pre>
50375085 <p>
5038 Converts an enumeration value into its integer tag type. When a tagged union is passed,
5039 the tag value is used as the enumeration value.
5086 Deprecated. Use {#link|@backingInt#} or {#link|@bitCast#} instead.
50405087 </p>
50415088 <p>
5042 If there is only one possible enum value, the result is a {#syntax#}comptime_int{#endsyntax#}
5043 known at {#link|comptime#}.
5089 Converts an enumeration value into its integer tag type. When a tagged union is passed,
5090 the tag value is used as the enumeration value.
50445091 </p>
50455092 {#see_also|@enumFromInt#}
50465093 {#header_close#}
......@@ -5077,7 +5124,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50775124 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer.
50785125 {#syntax#}value{#endsyntax#} can be {#syntax#}*T{#endsyntax#} or {#syntax#}?*T{#endsyntax#}.
50795126 </p>
5080 <p>To convert the other way, use {#link|@ptrFromInt#}</p>
5127 {#see_also|@ptrFromInt#}
50815128 {#header_close#}
50825129
50835130 {#header_open|@max#}
......@@ -5281,6 +5328,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52815328 If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}
52825329 is zero, this invokes safety-checked {#link|Illegal Behavior#}.
52835330 </p>
5331 {#see_also|@intFromPtr#}
52845332 {#header_close#}
52855333
52865334 {#header_open|@rem#}
doc/langref/runtime_invalid_enum_cast.zig+7-5
......@@ -1,16 +1,18 @@
11const std = @import("std");
22
3const Foo = enum {
3const Foo = enum(u2) {
44 a,
55 b,
66 c,
77};
88
9pub fn main() void {
10 var a: u2 = 3;
11 _ = &a;
12 const b: Foo = @enumFromInt(a);
9fn foo(a: u2) void {
10 const b: Foo = @fromBackingInt(a);
1311 std.debug.print("value: {s}\n", .{@tagName(b)});
1412}
1513
14pub fn main() void {
15 foo(3);
16}
17
1618// exe=fail
doc/langref/test_comptime_invalid_enum_cast.zig+2-2
......@@ -1,11 +1,11 @@
1const Foo = enum {
1const Foo = enum(u2) {
22 a,
33 b,
44 c,
55};
66comptime {
77 const a: u2 = 3;
8 const b: Foo = @enumFromInt(a);
8 const b: Foo = @fromBackingInt(a);
99 _ = b;
1010}
1111
doc/langref/test_enums.zig+17-11
......@@ -22,9 +22,9 @@ const Value = enum(u2) {
2222// Now you can cast between u2 and Value.
2323// The ordinal value starts from 0, counting up by 1 from the previous member.
2424test "enum ordinal value" {
25 try expectEqual(0, @intFromEnum(Value.zero));
26 try expectEqual(1, @intFromEnum(Value.one));
27 try expectEqual(2, @intFromEnum(Value.two));
25 try expectEqual(0, @backingInt(Value.zero));
26 try expectEqual(1, @backingInt(Value.one));
27 try expectEqual(2, @backingInt(Value.two));
2828}
2929
3030// You can override the ordinal value for an enum.
......@@ -34,9 +34,9 @@ const Value2 = enum(u32) {
3434 million = 1000000,
3535};
3636test "set enum ordinal value" {
37 try expectEqual(100, @intFromEnum(Value2.hundred));
38 try expectEqual(1000, @intFromEnum(Value2.thousand));
39 try expectEqual(1000000, @intFromEnum(Value2.million));
37 try expectEqual(100, @backingInt(Value2.hundred));
38 try expectEqual(1000, @backingInt(Value2.thousand));
39 try expectEqual(1000000, @backingInt(Value2.million));
4040}
4141
4242// You can also override only some values.
......@@ -48,11 +48,11 @@ const Value3 = enum(u4) {
4848 e,
4949};
5050test "enum implicit ordinal values and overridden values" {
51 try expectEqual(0, @intFromEnum(Value3.a));
52 try expectEqual(8, @intFromEnum(Value3.b));
53 try expectEqual(9, @intFromEnum(Value3.c));
54 try expectEqual(4, @intFromEnum(Value3.d));
55 try expectEqual(5, @intFromEnum(Value3.e));
51 try expectEqual(0, @backingInt(Value3.a));
52 try expectEqual(8, @backingInt(Value3.b));
53 try expectEqual(9, @backingInt(Value3.c));
54 try expectEqual(4, @backingInt(Value3.d));
55 try expectEqual(5, @backingInt(Value3.e));
5656}
5757
5858// Enums can have methods, the same as structs and unions.
......@@ -111,4 +111,10 @@ test "@tagName" {
111111 try expectEqualStrings(@tagName(Small.three), "three");
112112}
113113
114// Empty enums are uninstantiable, their tag type is always noreturn.
115const Empty = enum {};
116test "empty enum" {
117 try expectEqual(noreturn, @typeInfo(Empty).@"enum".tag_type);
118}
119
114120// test
doc/langref/test_inline_else.zig+2-2
......@@ -9,7 +9,7 @@ const SliceTypeB = extern struct {
99 ptr: [*]SliceTypeA,
1010 len: usize,
1111};
12const AnySlice = union(enum) {
12const AnySlice = union(enum(u8)) {
1313 a: SliceTypeA,
1414 b: SliceTypeB,
1515 c: []const u8,
......@@ -23,7 +23,7 @@ fn withFor(any: AnySlice) usize {
2323 // With `inline for` the function gets generated as
2424 // a series of `if` statements relying on the optimizer
2525 // to convert it to a switch.
26 if (field_value == @intFromEnum(any)) {
26 if (field_value == @backingInt(any)) {
2727 return @field(any, field_name).len;
2828 }
2929 }
doc/langref/test_packed_struct_backing_int.zig created+21
......@@ -0,0 +1,21 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const expectEqual = std.testing.expectEqual;
4
5const PackedStruct = packed struct(u8) {
6 lo: u4,
7 hi: u4,
8};
9
10test "convert to and from backing integer" {
11 const original: PackedStruct = .{ .lo = 0b1100, .hi = 0b0101 };
12
13 const backing_int = @backingInt(original);
14 comptime assert(@TypeOf(backing_int) == u8);
15 try expectEqual(0b0101_1100, backing_int);
16
17 const reconstructed: PackedStruct = @fromBackingInt(backing_int);
18 try expectEqual(original, reconstructed);
19}
20
21// test
doc/langref/test_tagged_union_with_tag_values.zig+2-2
......@@ -8,10 +8,10 @@ const Tagged = union(enum(u32)) {
88
99test "tag values" {
1010 const int: Tagged = .{ .int = -40 };
11 try expectEqual(123, @intFromEnum(int));
11 try expectEqual(123, @backingInt(int));
1212
1313 const boolean: Tagged = .{ .boolean = false };
14 try expectEqual(67, @intFromEnum(boolean));
14 try expectEqual(67, @backingInt(boolean));
1515}
1616
1717// test
lib/std/meta.zig+35-3
......@@ -408,7 +408,8 @@ pub fn FieldEnum(comptime T: type) type {
408408 else => {},
409409 }
410410
411 const IntTag = std.math.IntFittingRange(0, field_names.len -| 1);
411 if (field_names.len == 0) return enum {};
412 const IntTag = std.math.IntFittingRange(0, field_names.len - 1);
412413 return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len));
413414}
414415
......@@ -469,7 +470,8 @@ test FieldEnum {
469470
470471pub fn DeclEnum(comptime T: type) type {
471472 const decl_names = declarations(T);
472 const IntTag = std.math.IntFittingRange(0, decl_names.len -| 1);
473 if (decl_names.len == 0) return enum {};
474 const IntTag = std.math.IntFittingRange(0, decl_names.len - 1);
473475 return @Enum(IntTag, .exhaustive, decl_names, &std.simd.iota(IntTag, decl_names.len));
474476}
475477
......@@ -504,10 +506,40 @@ pub fn BareUnion(comptime T: type) type {
504506 .@"union" => |u| u,
505507 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),
506508 };
507
508509 return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);
509510}
510511
512/// For enums, packed unions and packed structs, returns their backing integer type.
513/// For tagged unions, returns the backing integer type of their enum tag type.
514pub fn BackingInt(comptime T: type) type {
515 switch (@typeInfo(T)) {
516 .@"enum" => |info| return info.tag_type,
517 .@"struct" => |info| if (info.backing_integer) |Int| return Int,
518 .@"union" => |info| switch (info.layout) {
519 .@"packed" => return info.backing_integer.?,
520 .auto => if (info.tag_type) |EnumTag|
521 return @typeInfo(EnumTag).@"enum".tag_type,
522 .@"extern" => {},
523 },
524 else => {},
525 }
526 @compileError("expected enum, tagged union, packed union or packed struct type, found '" ++ @typeName(T) ++ "'");
527}
528
529test BackingInt {
530 const E = enum(u8) { a, b, c };
531 try testing.expect(BackingInt(E) == u8);
532
533 const S = packed struct(u16) { x: u8, y: i8 };
534 try testing.expect(BackingInt(S) == u16);
535
536 const U = packed union(i32) { a: u32, b: enum(i32) { _ } };
537 try testing.expect(BackingInt(U) == i32);
538
539 const T = union(enum(i8)) { a, b, c };
540 try testing.expect(BackingInt(T) == i8);
541}
542
511543pub fn Tag(comptime T: type) type {
512544 return switch (@typeInfo(T)) {
513545 .@"enum" => |info| info.tag_type,
lib/std/zig.zig-2
......@@ -906,7 +906,6 @@ pub const SimpleComptimeReason = enum(u32) {
906906 slice_single_item_ptr_bounds,
907907 stored_to_comptime_field,
908908 stored_to_comptime_var,
909 casted_to_comptime_enum,
910909 casted_to_comptime_int,
911910 casted_to_comptime_float,
912911 std_lang_decl,
......@@ -989,7 +988,6 @@ pub const SimpleComptimeReason = enum(u32) {
989988 .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds",
990989 .stored_to_comptime_field => "value stored to a comptime field must be comptime-known",
991990 .stored_to_comptime_var => "value stored to a comptime variable must be comptime-known",
992 .casted_to_comptime_enum => "value casted to enum with 'comptime_int' tag type must be comptime-known",
993991 .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known",
994992 .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known",
995993 .std_lang_decl => "'std.lang' declaration values must be comptime-known",
lib/std/zig/Ast/Render.zig+19-2
......@@ -1652,7 +1652,17 @@ fn renderBuiltinCall(
16521652 const tree = r.tree;
16531653 const ais = r.ais;
16541654
1655 try renderToken(r, builtin_token, .none); // @name
1655 // remove before 0.18.0 is released
1656 const builtin_token_slice = tree.tokenSlice(builtin_token); // @name
1657 const lexeme: []const u8, const have_int_cast: bool = lexeme: {
1658 if (mem.eql(u8, builtin_token_slice, "@intFromEnum"))
1659 break :lexeme .{ "@backingInt", false };
1660 if (mem.eql(u8, builtin_token_slice, "@enumFromInt"))
1661 break :lexeme .{ "@fromBackingInt(@intCast", true };
1662 break :lexeme .{ builtin_token_slice, false };
1663 };
1664 try ais.writeAll(lexeme);
1665 try renderSpace(r, builtin_token, builtin_token_slice.len, .none);
16561666
16571667 if (r.fixups.rebase_imported_paths) |prefix| {
16581668 const slice = tree.tokenSlice(builtin_token);
......@@ -1675,7 +1685,14 @@ fn renderBuiltinCall(
16751685 }
16761686 }
16771687
1678 return renderParamList(r, builtin_token + 1, params, space);
1688 try renderParamList(r, builtin_token + 1, params, .skip); // space is rendered below
1689 if (have_int_cast) try ais.writeAll(")");
1690 const rparen: Ast.TokenIndex = rparen: {
1691 if (params.len == 0) break :rparen builtin_token + 1 + 1;
1692 const after_last_param_tok = tree.lastToken(params[params.len - 1]) + 1;
1693 break :rparen after_last_param_tok + @intFromBool(tree.tokenTag(after_last_param_tok) == .comma);
1694 };
1695 return renderSpace(r, rparen, tokenSliceForRender(tree, rparen).len, space);
16791696}
16801697
16811698fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex {
lib/std/zig/AstGen.zig+18
......@@ -2703,6 +2703,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
27032703 .elem_type,
27042704 .indexable_ptr_elem_type,
27052705 .splat_op_result_ty,
2706 .from_backing_int_arg_ty,
27062707 .reify_int,
27072708 .vector_type,
27082709 .indexable_ptr_len,
......@@ -2803,6 +2804,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28032804 .error_set_decl,
28042805 .enum_from_int,
28052806 .int_from_enum,
2807 .backing_int,
2808 .from_backing_int,
28062809 .type_info,
28072810 .size_of,
28082811 .bit_size_of,
......@@ -9168,6 +9171,7 @@ fn builtinCall(
91689171 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
91699172 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
91709173 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),
9174 .backing_int => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .backing_int),
91719175 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
91729176 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
91739177 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
......@@ -9199,6 +9203,20 @@ fn builtinCall(
91999203 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
92009204 // zig fmt: on
92019205
9206 .from_backing_int => {
9207 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9208 const result_ty = try ri.rl.resultTypeForCast(gz, node, builtin_name);
9209 const backing_int_ty = try gz.addUnNode(.from_backing_int_arg_ty, result_ty, node);
9210 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = backing_int_ty } }, params[0]);
9211
9212 try emitDbgStmt(gz, cursor);
9213 const result = try gz.addPlNode(.from_backing_int, node, Zir.Inst.Bin{
9214 .lhs = result_ty,
9215 .rhs = operand,
9216 });
9217 return rvalue(gz, ri, result, node);
9218 },
9219
92029220 .in_comptime => if (gz.is_comptime) {
92039221 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
92049222 } else {
lib/std/zig/AstRlAnnotate.zig+2
......@@ -889,6 +889,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
889889 .int_from_bool,
890890 .int_from_error,
891891 .error_from_int,
892 .from_backing_int,
892893 .embed_file,
893894 .error_name,
894895 .set_runtime_safety,
......@@ -916,6 +917,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
916917 .float_from_int,
917918 .ptr_from_int,
918919 .enum_from_int,
920 .backing_int,
919921 .float_cast,
920922 .int_cast,
921923 .truncate,
lib/std/zig/BuiltinFn.zig+16
......@@ -56,6 +56,8 @@ pub const Tag = enum {
5656 import,
5757 in_comptime,
5858 int_cast,
59 backing_int,
60 from_backing_int,
5961 enum_from_int,
6062 error_from_int,
6163 float_from_int,
......@@ -564,6 +566,20 @@ pub const list = list: {
564566 .param_count = 1,
565567 },
566568 },
569 .{
570 "@backingInt",
571 .{
572 .tag = .backing_int,
573 .param_count = 1,
574 },
575 },
576 .{
577 "@fromBackingInt",
578 .{
579 .tag = .from_backing_int,
580 .param_count = 1,
581 },
582 },
567583 .{
568584 "@enumFromInt",
569585 .{
lib/std/zig/Zir.zig+28-2
......@@ -283,6 +283,14 @@ pub const Inst = struct {
283283 ///
284284 /// Uses the `un_node` field.
285285 splat_op_result_ty,
286 /// Given a type, strips away any error unions or optionals stacked
287 /// on top, validates it for usage with `@fromBackingInt` and returns
288 /// its backing integer type.
289 ///
290 /// `E!?enum(T) { _ }` -> `T`
291 ///
292 /// Uses the `un_node` field.
293 from_backing_int_arg_ty,
286294 /// Given a pointer to an indexable object, returns the len property. This is
287295 /// used by for loops. This instruction also emits a for-loop specific compile
288296 /// error if the indexable object is not indexable.
......@@ -872,6 +880,9 @@ pub const Inst = struct {
872880 /// Converts an enum value into an integer. Resulting type will be the tag type
873881 /// of the enum. Uses `un_node`.
874882 int_from_enum,
883 /// Implements the `@backingInt` builtin.
884 /// Uses `un_node`.
885 backing_int,
875886 /// Implement builtin `@alignOf`. Uses `un_node`.
876887 align_of,
877888 /// Implement builtin `@intFromBool`. Uses `un_node`.
......@@ -934,6 +945,9 @@ pub const Inst = struct {
934945 /// Converts an integer into an enum value.
935946 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
936947 enum_from_int,
948 /// Implements the `@fromBackingInt` builtin.
949 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
950 from_backing_int,
937951 /// Convert a larger float type to any other float type, possibly causing
938952 /// a loss of precision.
939953 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
......@@ -1115,6 +1129,7 @@ pub const Inst = struct {
11151129 .elem_type,
11161130 .indexable_ptr_elem_type,
11171131 .splat_op_result_ty,
1132 .from_backing_int_arg_ty,
11181133 .indexable_ptr_len,
11191134 .anyframe_type,
11201135 .as_node,
......@@ -1229,6 +1244,8 @@ pub const Inst = struct {
12291244 .field_type_ref,
12301245 .enum_from_int,
12311246 .int_from_enum,
1247 .backing_int,
1248 .from_backing_int,
12321249 .type_info,
12331250 .size_of,
12341251 .bit_size_of,
......@@ -1409,6 +1426,7 @@ pub const Inst = struct {
14091426 .elem_type,
14101427 .indexable_ptr_elem_type,
14111428 .splat_op_result_ty,
1429 .from_backing_int_arg_ty,
14121430 .indexable_ptr_len,
14131431 .anyframe_type,
14141432 .as_node,
......@@ -1511,6 +1529,8 @@ pub const Inst = struct {
15111529 .field_type_ref,
15121530 .enum_from_int,
15131531 .int_from_enum,
1532 .backing_int,
1533 .from_backing_int,
15141534 .type_info,
15151535 .size_of,
15161536 .bit_size_of,
......@@ -1645,6 +1665,7 @@ pub const Inst = struct {
16451665 .elem_type = .un_node,
16461666 .indexable_ptr_elem_type = .un_node,
16471667 .splat_op_result_ty = .un_node,
1668 .from_backing_int_arg_ty = .un_node,
16481669 .indexable_ptr_len = .un_node,
16491670 .anyframe_type = .un_node,
16501671 .as_node = .pl_node,
......@@ -1774,6 +1795,7 @@ pub const Inst = struct {
17741795 .compile_error = .un_node,
17751796 .set_eval_branch_quota = .un_node,
17761797 .int_from_enum = .un_node,
1798 .backing_int = .un_node,
17771799 .align_of = .un_node,
17781800 .int_from_bool = .un_node,
17791801 .embed_file = .un_node,
......@@ -1803,6 +1825,7 @@ pub const Inst = struct {
18031825 .float_from_int = .pl_node,
18041826 .ptr_from_int = .pl_node,
18051827 .enum_from_int = .pl_node,
1828 .from_backing_int = .pl_node,
18061829 .float_cast = .pl_node,
18071830 .int_cast = .pl_node,
18081831 .ptr_cast = .pl_node,
......@@ -2160,8 +2183,8 @@ pub const Inst = struct {
21602183 astgen_error,
21612184 /// Given a type, strips away any error unions or optionals stacked
21622185 /// on top and returns the base type. That base type must be a float.
2163 /// For example: Provided with error{Foo}!?f64, returns f64.
2164 /// `operand` is `operand: Air.Inst.Ref`.
2186 /// For example: Provided with `error{Foo}!?f64`, returns `f64`.
2187 /// `operand` is payload index to `UnNode`.
21652188 float_op_result_ty,
21662189
21672190 pub const InstData = struct {
......@@ -4141,6 +4164,7 @@ fn findTrackableInner(
41414164 .elem_type,
41424165 .indexable_ptr_elem_type,
41434166 .splat_op_result_ty,
4167 .from_backing_int_arg_ty,
41444168 .indexable_ptr_len,
41454169 .anyframe_type,
41464170 .as_node,
......@@ -4296,6 +4320,8 @@ fn findTrackableInner(
42964320 .float_from_int,
42974321 .ptr_from_int,
42984322 .enum_from_int,
4323 .backing_int,
4324 .from_backing_int,
42994325 .float_cast,
43004326 .int_cast,
43014327 .ptr_cast,
lib/std/zig/parser_test.zig+61-1
......@@ -1120,7 +1120,7 @@ test "zig fmt: empty enum decls" {
11201120 \\const A = enum {};
11211121 \\const B = enum(u32) {};
11221122 \\const C = extern enum(c_int) {};
1123 \\const D = packed enum(u8) {};
1123 \\const D = packed enum(noreturn) {};
11241124 \\
11251125 );
11261126}
......@@ -6938,6 +6938,66 @@ test "zig fmt: inner over-indented if expressions becoming multiline" {
69386938 );
69396939}
69406940
6941test "zig fmt: canonicalize @intFromEnum(x) to @backingInt(x)" {
6942 try testTransform(
6943 \\const a = @intFromEnum(x);
6944 \\
6945 \\const b = @intFromEnum(
6946 \\ x,
6947 \\);
6948 \\
6949 \\const c = @intFromEnum(x); // comment preserved
6950 \\
6951 \\const d = @intFromEnum( // comment 1 preserved
6952 \\ x, // comment 2 preserved
6953 \\); // comment 3 preserved
6954 \\
6955 ,
6956 \\const a = @backingInt(x);
6957 \\
6958 \\const b = @backingInt(
6959 \\ x,
6960 \\);
6961 \\
6962 \\const c = @backingInt(x); // comment preserved
6963 \\
6964 \\const d = @backingInt( // comment 1 preserved
6965 \\ x, // comment 2 preserved
6966 \\); // comment 3 preserved
6967 \\
6968 );
6969}
6970
6971test "zig fmt: canonicalize @enumFromInt(x) to @fromBackingInt(@intCast(x))" {
6972 try testTransform(
6973 \\const a: E = @enumFromInt(x);
6974 \\
6975 \\const b: E = @enumFromInt(
6976 \\ x,
6977 \\);
6978 \\
6979 \\const c: E = @enumFromInt(x); // comment preserved
6980 \\
6981 \\const d: E = @enumFromInt( // comment 1 preserved
6982 \\ x, // comment 2 preserved
6983 \\); // comment 3 preserved
6984 \\
6985 ,
6986 \\const a: E = @fromBackingInt(@intCast(x));
6987 \\
6988 \\const b: E = @fromBackingInt(@intCast(
6989 \\ x,
6990 \\));
6991 \\
6992 \\const c: E = @fromBackingInt(@intCast(x)); // comment preserved
6993 \\
6994 \\const d: E = @fromBackingInt(@intCast( // comment 1 preserved
6995 \\ x, // comment 2 preserved
6996 \\)); // comment 3 preserved
6997 \\
6998 );
6999}
7000
69417001test "recovery: top level" {
69427002 try testError(
69437003 \\test "" {inline}
src/Air.zig+6
......@@ -288,6 +288,10 @@ pub const Inst = struct {
288288 ///
289289 /// Uses the `ty_op` field.
290290 bit_cast,
291 /// Like `bit_cast`, but triggers a safety panic if the destination type is an exhaustive
292 /// enum and the operand is not a valid value of this type;
293 /// i.e. equivalent to a safety check based on `.is_named_enum_value`
294 bit_cast_safe,
291295 /// Cast a pointer to a different pointer type. The result type is a slice iff the operand
292296 /// type is a slice (the length of the slice does not change). All other pointer attributes
293297 /// except for the address space may change.
......@@ -1717,6 +1721,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
17171721
17181722 .not,
17191723 .bit_cast,
1724 .bit_cast_safe,
17201725 .ptr_cast,
17211726 .ptr_from_int,
17221727 .int_from_ptr,
......@@ -1969,6 +1974,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
19691974 .add_safe,
19701975 .sub_safe,
19711976 .mul_safe,
1977 .bit_cast_safe,
19721978 .int_cast_safe,
19731979 .int_from_float_safe,
19741980 .int_from_float_optimized_safe,
src/Air/Legalize.zig+116-14
......@@ -156,6 +156,10 @@ pub const Feature = enum {
156156 /// Legalize splat to a one element vector to a bitcast.
157157 splat_one_elem_to_bit_cast,
158158
159 /// Replace `bit_cast_safe` with an explicit safety check which `call`s the panic function on failure.
160 /// `scalarize_*` variants for `bit_cast_safe` do not exist since the safety check is only desired if the result
161 /// type is a scalar enum type, so the scalarizatins for regular `bit_cast` are exactly equivalent.
162 expand_bit_cast_safe,
159163 /// Replace `int_cast_safe` with an explicit safety check which `call`s the panic function on failure.
160164 /// Not compatible with `scalarize_int_cast_safe`.
161165 expand_int_cast_safe,
......@@ -600,9 +604,28 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
600604 continue :inst l.replaceInst(inst, .block, payload);
601605 }
602606 },
607 .bit_cast_safe => if (l.features.has(.expand_bit_cast_safe)) {
608 if (try l.safeBitcastBlockPayload(inst)) |payload| {
609 continue :inst l.replaceInst(inst, .block, payload);
610 }
611 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
612 continue :inst l.replaceInst(inst, .bit_cast, .{ .ty_op = ty_op });
613 } else if (l.features.hasAny(&.{
614 .scalarize_bit_cast_array,
615 .scalarize_bit_cast_vector_non_elementwise,
616 .scalarize_bit_cast_padded_elems,
617 })) {
618 if (try l.scalarizeBitcastBlockPayload(inst)) |payload| {
619 continue :inst l.replaceInst(inst, .block, payload);
620 }
621 },
603622 .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) {
604623 assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both
605 continue :inst l.replaceInst(inst, .block, try l.safeIntcastBlockPayload(inst));
624 if (try l.safeIntcastBlockPayload(inst)) |payload| {
625 continue :inst l.replaceInst(inst, .block, payload);
626 }
627 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
628 continue :inst l.replaceInst(inst, .int_cast, .{ .ty_op = ty_op });
606629 } else if (l.features.has(.scalarize_int_cast_safe)) {
607630 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
608631 if (ty_op.ty.toType().isVector(zcu)) {
......@@ -1006,7 +1029,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
10061029
10071030 if (result_is_array) {
10081031 // This is only allowed when legalizing an elementwise bitcast.
1009 assert(orig.tag == .bit_cast);
1032 switch (orig.tag) {
1033 .bit_cast, .bit_cast_safe => {},
1034 else => unreachable,
1035 }
10101036 assert(form == .ty_op);
10111037 }
10121038
......@@ -1076,7 +1102,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
10761102 orig_operand,
10771103 index_val,
10781104 ).toRef();
1079 break :elem loop.block.addTyOp(l, orig.tag, res_elem_ty, operand).toRef();
1105 const scalar_tag: Air.Inst.Tag = switch (orig.tag) {
1106 .bit_cast_safe => .bit_cast, // safety check is not supposed to be elementwise
1107 else => orig.tag,
1108 };
1109 break :elem loop.block.addTyOp(l, scalar_tag, res_elem_ty, operand).toRef();
10801110 },
10811111 .bin_op => elem: {
10821112 const orig_bin = orig.data.bin_op;
......@@ -1700,9 +1730,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?
17001730
17011731 // Now convert `uint_ty` (`uN`) to `dest_ty`.
17021732
1733 // We omit the safety check when casting to an array or a vector since it's
1734 // not supposed to be elementwise.
1735 if (dest_ty.zigTypeTag(zcu) == .@"enum") assert(int_to_dest_ok);
1736
17031737 if (int_to_dest_ok) {
17041738 _ = main_block.stealCapacity(17);
1705 const result = main_block.addBitCast(l, dest_ty, uint_val);
1739 const result = switch (l.air_instructions.items(.tag)[@intFromEnum(orig_inst)]) {
1740 .bit_cast => main_block.addBitCast(l, dest_ty, uint_val),
1741 .bit_cast_safe => main_block.add(l, .{
1742 .tag = .bit_cast_safe,
1743 .data = .{ .ty_op = .{
1744 .ty = .fromType(dest_ty),
1745 .operand = uint_val,
1746 } },
1747 }).toRef(),
1748 else => unreachable,
1749 };
17061750 main_block.addBr(l, orig_inst, result);
17071751 } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) {
17081752 _ = main_block.stealCapacity(16);
......@@ -2089,7 +2133,62 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize
20892133 } };
20902134}
20912135
2092fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2136fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?Air.Inst.Data {
2137 const pt = l.pt;
2138 const zcu = pt.zcu;
2139 const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op;
2140
2141 const operand_ref = ty_op.operand;
2142 const dest_ty = ty_op.ty.toType();
2143
2144 if (dest_ty.zigTypeTag(zcu) != .@"enum" or
2145 dest_ty.isNonexhaustiveEnum(zcu) or
2146 !zcu.backendSupportsFeature(.is_named_enum_value))
2147 {
2148 return null;
2149 }
2150
2151 // We are building this:
2152 //
2153 // %x = block({
2154 // %1 = bit_cast(@res_ty, %y)
2155 // %2 = is_named_enum_value(%1)
2156 // %3 = cond_br(%2, {
2157 // %4 = br(%x, %1)
2158 // }, {
2159 // %5 = call(@panic.invalidEnumValue, [])
2160 // %6 = unreach()
2161 // })
2162 // })
2163
2164 var inst_buf: [6]Air.Inst.Index = undefined;
2165 try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len);
2166
2167 var block: Block = .init(&inst_buf);
2168
2169 const cast_inst = block.addBitCast(l, dest_ty, operand_ref);
2170 const is_named_inst = block.add(l, .{
2171 .tag = .is_named_enum_value,
2172 .data = .{ .un_op = cast_inst },
2173 });
2174
2175 var condbr: CondBr = .init(l, is_named_inst.toRef(), &block, .{ .false = .cold });
2176
2177 condbr.then_block = .init(block.stealRemainingCapacity());
2178 condbr.then_block.addBr(l, orig_inst, cast_inst);
2179
2180 condbr.else_block = .init(condbr.then_block.stealRemainingCapacity());
2181 try condbr.else_block.addPanic(l, .invalid_enum_value);
2182
2183 try condbr.finish(l);
2184
2185 return .{ .ty_pl = .{
2186 .ty = .fromType(dest_ty),
2187 .payload = try l.addBlockBody(block.body()),
2188 } };
2189}
2190
2191fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?Air.Inst.Data {
20932192 const pt = l.pt;
20942193 const zcu = pt.zcu;
20952194 const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op;
......@@ -2108,6 +2207,9 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
21082207 .@"enum" => true,
21092208 else => unreachable,
21102209 };
2210 const have_enum_value_check = dest_is_enum and
2211 !dest_ty.isNonexhaustiveEnum(zcu) and
2212 zcu.backendSupportsFeature(.is_named_enum_value);
21112213
21122214 const operand_info = operand_scalar_ty.intInfo(zcu);
21132215 const dest_info = dest_scalar_ty.intInfo(zcu);
......@@ -2123,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
21232225 };
21242226 };
21252227
2228 if (!have_enum_value_check and !have_min_check and !have_max_check) {
2229 return null;
2230 }
2231
21262232 // The worst-case scenario in terms of total instructions and total condbrs is the case where
21272233 // the result type is an exhaustive enum whose tag type is smaller than the operand type:
21282234 //
......@@ -2172,7 +2278,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
21722278 const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds;
21732279
21742280 if (have_min_check or have_max_check) {
2175 const dest_int_ty = if (dest_is_enum) dest_ty.intTagType(zcu) else dest_ty;
2281 const dest_int_ty = if (dest_is_enum) dest_ty.backingIntType(zcu) else dest_ty;
21762282 const condbr = &condbr_buf[condbr_idx];
21772283 condbr_idx += 1;
21782284 const below_min_inst: Air.Inst.Index = if (have_min_check) inst: {
......@@ -2218,7 +2324,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
22182324 } },
22192325 });
22202326 // For ints we're already done, but for exhaustive enums we must check this is a valid tag.
2221 if (dest_is_enum and !dest_ty.isNonexhaustiveEnum(zcu) and zcu.backendSupportsFeature(.is_named_enum_value)) {
2327 if (have_enum_value_check) {
22222328 assert(!is_vector); // vectors of enums don't exist
22232329 // We are building this:
22242330 // %1 = is_named_enum_value(%cast_inst)
......@@ -2240,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
22402346 cur_block = &condbr.then_block;
22412347 }
22422348 // Finally, just `br` to our outer `block`.
2243 _ = cur_block.add(l, .{
2244 .tag = .br,
2245 .data = .{ .br = .{
2246 .block_inst = orig_inst,
2247 .operand = cast_inst.toRef(),
2248 } },
2249 });
2349 cur_block.addBr(l, orig_inst, cast_inst.toRef());
2350
22502351 // We might not have used all of the instructions; that's intentional.
22512352 _ = cur_block.stealRemainingCapacity();
22522353
2354 assert(condbr_idx != 0); // should have already returned `null`
22532355 for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l);
22542356 return .{ .ty_pl = .{
22552357 .ty = Air.internedToRef(dest_ty.toIntern()),
src/Air/Liveness.zig+1
......@@ -491,6 +491,7 @@ fn analyzeInst(
491491
492492 .not,
493493 .bit_cast,
494 .bit_cast_safe,
494495 .ptr_cast,
495496 .ptr_from_int,
496497 .int_from_ptr,
src/Air/Liveness/Verify.zig+1
......@@ -79,6 +79,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
7979 // unary
8080 .not,
8181 .bit_cast,
82 .bit_cast_safe,
8283 .ptr_cast,
8384 .ptr_from_int,
8485 .int_from_ptr,
src/Air/Verify.zig+1-1
......@@ -118,7 +118,7 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
118118 if (ptr_ty.childType(zcu).toIntern() != verify.ret_ty.toIntern()) return verify.fail("bad return type");
119119 },
120120
121 .bit_cast => {
121 .bit_cast, .bit_cast_safe => {
122122 const ty_op = data[@intFromEnum(inst)].ty_op;
123123 const operand_ty = air.typeOf(ty_op.operand, ip);
124124 const result_ty = ty_op.ty.toType();
src/Air/print.zig+1
......@@ -231,6 +231,7 @@ const Writer = struct {
231231
232232 .not,
233233 .bit_cast,
234 .bit_cast_safe,
234235 .ptr_cast,
235236 .ptr_from_int,
236237 .int_from_ptr,
src/InternPool.zig+3-7
......@@ -5040,7 +5040,6 @@ pub const Tag = enum(u8) {
50405040 /// The set of values that are encoded this way is:
50415041 /// * An array or vector which has length 0.
50425042 /// * A struct which has all fields comptime-known.
5043 /// * An empty enum or union. TODO: this value's existence is strange, because such a type in reality has no values. See #15909
50445043 /// data is Index of the type, which is known to be zero bits at runtime.
50455044 only_possible_value,
50465045 /// data is extra index to Key.Union.
......@@ -7740,12 +7739,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
77407739 }),
77417740
77427741 .enum_tag => |enum_tag| {
7743 assert(ip.isEnumType(enum_tag.ty));
7744 switch (ip.indexToKey(enum_tag.ty)) {
7745 .simple_type => assert(ip.isIntegerType(ip.typeOf(enum_tag.int))),
7746 .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).int_tag_type),
7747 else => unreachable,
7748 }
7742 const enum_obj = ip.loadEnumType(enum_tag.ty);
7743 assert(ip.typeOf(enum_tag.int) == enum_obj.int_tag_type);
77497744 items.appendAssumeCapacity(.{
77507745 .tag = .enum_tag,
77517746 .data = try addExtra(extra, enum_tag),
......@@ -10197,6 +10192,7 @@ pub fn getCoerced(
1019710192 .enum_type => {
1019810193 const enum_type = ip.loadEnumType(new_ty);
1019910194 const index = enum_type.nameIndex(ip, enum_literal).?;
10195 assert(enum_type.int_tag_type != .noreturn_type);
1020010196 return ip.get(gpa, io, tid, .{ .enum_tag = .{
1020110197 .ty = new_ty,
1020210198 .int = if (enum_type.field_values.len != 0)
src/Sema.zig+248-99
......@@ -1191,11 +1191,14 @@ fn analyzeBodyInner(
11911191 .elem_type => try sema.zirElemType(block, inst),
11921192 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
11931193 .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst),
1194 .from_backing_int_arg_ty => try sema.zirFromBackingIntArgTy(block, inst),
11941195 .enum_literal => try sema.zirEnumLiteral(block, inst),
11951196 .decl_literal => try sema.zirDeclLiteral(block, inst, true),
11961197 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
11971198 .int_from_enum => try sema.zirIntFromEnum(block, inst),
11981199 .enum_from_int => try sema.zirEnumFromInt(block, inst),
1200 .backing_int => try sema.zirBackingInt(block, inst),
1201 .from_backing_int => try sema.zirFromBackingInt(block, inst),
11991202 .err_union_code => try sema.zirErrUnionCode(block, inst),
12001203 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
12011204 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst),
......@@ -2428,6 +2431,28 @@ fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src:
24282431 });
24292432}
24302433
2434fn failWithAmbiguousBackingIntType(
2435 sema: *Sema,
2436 block: *Block,
2437 src: LazySrcLoc,
2438 int_backed_ty: Type,
2439 builtin_name: []const u8,
2440) CompileError {
2441 const pt = sema.pt;
2442 const zcu = pt.zcu;
2443 return sema.failWithOwnedErrorMsg(block, msg: {
2444 const msg = try sema.errMsg(src, "{s} is ambiguous for type '{f}'", .{
2445 builtin_name, int_backed_ty.fmt(pt),
2446 });
2447 errdefer msg.destroy(sema.gpa);
2448 try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "backing integer type of {t} is inferred", .{
2449 int_backed_ty.zigTypeTag(zcu),
2450 });
2451 try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "consider explicitly specifying the backing integer type", .{});
2452 break :msg msg;
2453 });
2454}
2455
24312456fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {
24322457 const pt = sema.pt;
24332458 const msg = msg: {
......@@ -4782,7 +4807,7 @@ fn failWithBadUnionFieldAccess(
47824807 return sema.failWithOwnedErrorMsg(block, msg);
47834808}
47844809
4785pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void {
4810pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) Allocator.Error!void {
47864811 const zcu = sema.pt.zcu;
47874812 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
47884813 const category = switch (decl_ty.zigTypeTag(zcu)) {
......@@ -7853,19 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
78537878 },
78547879 };
78557880 const enum_tag_ty = sema.typeOf(enum_tag);
7856 const int_tag_ty = enum_tag_ty.intTagType(zcu);
7857
7858 // TODO: use correct solution
7859 // https://github.com/ziglang/zig/issues/15909
7860 if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) {
7861 return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{
7862 enum_tag_ty.fmt(pt),
7863 });
7864 }
7881 const int_tag_ty = enum_tag_ty.backingIntType(zcu);
7882 assert(int_tag_ty.classify(zcu) != .no_possible_value);
78657883
78667884 if (sema.resolveValue(enum_tag)) |enum_tag_val| {
78677885 if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty);
7868 return .fromValue(enum_tag_val.intFromEnum(zcu));
7886 return .fromValue(enum_tag_val.backingInt(zcu));
78697887 }
78707888
78717889 try sema.requireRuntimeBlock(block, src, operand_src);
......@@ -7891,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
78917909
78927910 if (sema.resolveValue(operand)) |int_val| {
78937911 if (dest_ty.isNonexhaustiveEnum(zcu)) {
7894 const int_tag_ty = dest_ty.intTagType(zcu);
7912 const int_tag_ty = dest_ty.backingIntType(zcu);
78957913 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {
78967914 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
78977915 }
......@@ -7910,15 +7928,11 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
79107928 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
79117929 }
79127930
7913 if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) {
7914 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum });
7915 }
7916
79177931 if (try dest_ty.onePossibleValue(pt)) |opv| {
79187932 if (block.wantSafety()) {
79197933 // The operand is runtime-known but the result is comptime-known. In
79207934 // this case we still need a safety check.
7921 const expect_int = try pt.getCoerced(opv.intFromEnum(zcu), operand_ty);
7935 const expect_int = try pt.getCoerced(opv.backingInt(zcu), operand_ty);
79227936 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));
79237937 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
79247938 }
......@@ -9372,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
93729386 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),
93739387 else => {},
93749388 }
9375
93769389 break :msg msg;
93779390 }),
93789391 .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {
......@@ -9407,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
94079420 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
94089421 }
94099422
9410 return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node));
9423 operand_ty.assertHasLayout(zcu);
9424 try sema.ensureLayoutResolved(dest_ty, src, .init);
9425
9426 const operand_bits = operand_ty.bitSize(zcu);
9427 const dest_bits = dest_ty.bitSize(zcu);
9428 if (operand_bits != dest_bits) {
9429 return sema.fail(block, src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
9430 dest_ty.fmt(pt),
9431 dest_bits,
9432 operand_ty.fmt(pt),
9433 operand_bits,
9434 });
9435 }
9436
9437 if (sema.resolveValue(operand)) |operand_val| {
9438 const dest_is_exhaustive_enum = dest_ty.zigTypeTag(zcu) == .@"enum" and
9439 !dest_ty.isNonexhaustiveEnum(zcu);
9440 if (dest_is_exhaustive_enum and operand_val.isUndef(zcu)) {
9441 return sema.failWithUseOfUndef(block, operand_src, null);
9442 }
9443
9444 const result_val = try sema.bitCastVal(operand_val, dest_ty);
9445
9446 if (dest_is_exhaustive_enum and
9447 dest_ty.enumTagFieldIndex(result_val, zcu) == null)
9448 {
9449 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
9450 dest_ty.fmt(pt), result_val.backingInt(zcu).fmtValueSema(pt, sema),
9451 });
9452 }
9453
9454 return .fromValue(result_val);
9455 }
9456
9457 try sema.validateRuntimeValue(block, src, operand);
9458
9459 if (block.wantSafety()) {
9460 try sema.preparePanicId(src, .invalid_enum_value);
9461 return block.addTyOp(.bit_cast_safe, dest_ty, operand);
9462 }
9463 return block.addTyOp(.bit_cast, dest_ty, operand);
9464}
9465
9466fn zirBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9467 const pt = sema.pt;
9468 const zcu = pt.zcu;
9469 const gpa = zcu.comp.gpa;
9470 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
9471 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9472
9473 const operand = sema.resolveInst(inst_data.operand);
9474 const operand_ty = sema.typeOf(operand);
9475 operand_ty.assertHasLayout(zcu);
9476 const int_backed_ref: Air.Inst.Ref = ref: switch (operand_ty.zigTypeTag(zcu)) {
9477 .@"enum" => operand,
9478 .@"union" => {
9479 const union_obj = zcu.intern_pool.loadUnionType(operand_ty.toIntern());
9480 if (union_obj.tag_usage == .tagged) break :ref try sema.unionToTag(block, operand);
9481 if (union_obj.layout == .@"packed") break :ref operand;
9482 return sema.failWithOwnedErrorMsg(block, msg: {
9483 const msg = try sema.errMsg(operand_src, "non-packed union '{f}' does not have a backing integer", .{operand_ty.fmt(pt)});
9484 errdefer msg.deinit(gpa);
9485 try sema.errNote(operand_src, msg, "untagged union '{f}' does not have an enum tag with a backing integer", .{operand_ty.fmt(pt)});
9486 try sema.addDeclaredHereNote(msg, operand_ty);
9487 break :msg msg;
9488 });
9489 },
9490 .@"struct" => {
9491 if (operand_ty.containerLayout(zcu) != .@"packed") {
9492 return sema.fail(block, operand_src, "non-packed struct '{f}' does not have a backing integer", .{
9493 operand_ty.fmt(pt),
9494 });
9495 }
9496 break :ref operand;
9497 },
9498 else => {
9499 return sema.fail(block, operand_src, "expected enum, tagged union, packed union or packed struct, found '{f}'", .{
9500 operand_ty.fmt(pt),
9501 });
9502 },
9503 };
9504 const int_backed_ty = sema.typeOf(int_backed_ref);
9505 if (int_backed_ty.backingIntMode(zcu) != .explicit and int_backed_ty.zigTypeTag(zcu) != .@"enum")
9506 return sema.failWithAmbiguousBackingIntType(block, operand_src, int_backed_ty, "@backingInt");
9507 const backing_int_ty = int_backed_ty.backingIntType(zcu);
9508
9509 if (sema.resolveValue(int_backed_ref)) |int_backed_val| {
9510 if (int_backed_val.isUndef(zcu)) return pt.undefRef(backing_int_ty);
9511 return .fromValue(int_backed_val.backingInt(zcu));
9512 }
9513
9514 switch (backing_int_ty.classify(zcu)) {
9515 .partially_comptime, .fully_comptime => unreachable, // does not apply to integers
9516 .no_possible_value => unreachable, // enum also NPV, cannot instantiate NPV types
9517 .one_possible_value => unreachable, // enum or bitpack also OPV, should have been resolve above
9518 .runtime => {},
9519 }
9520
9521 return block.addTyOp(.bit_cast, backing_int_ty, int_backed_ref);
9522}
9523
9524fn zirFromBackingIntArgTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9525 const pt = sema.pt;
9526 const zcu = pt.zcu;
9527 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
9528 const src = block.nodeOffset(inst_data.src_node);
9529
9530 const dest_ty = try sema.resolveDestType(block, src, inst_data.operand, .remove_eu_opt, "@fromBackingInt");
9531 try sema.ensureLayoutResolved(dest_ty, src, .init);
9532 switch (dest_ty.zigTypeTag(zcu)) {
9533 .@"enum" => {},
9534 .@"struct", .@"union" => |type_tag| if (dest_ty.containerLayout(zcu) != .@"packed") {
9535 return sema.fail(block, src, "non-packed {t} '{f}' does not have a backing integer", .{
9536 type_tag, dest_ty.fmt(pt),
9537 });
9538 },
9539 else => {
9540 return sema.fail(block, src, "expected enum, packed union or packed struct, found '{f}'", .{
9541 dest_ty.fmt(pt),
9542 });
9543 },
9544 }
9545 if (dest_ty.backingIntMode(zcu) != .explicit and dest_ty.zigTypeTag(zcu) != .@"enum")
9546 return sema.failWithAmbiguousBackingIntType(block, src, dest_ty, "@fromBackingInt");
9547 return .fromType(dest_ty.backingIntType(zcu));
9548}
9549
9550fn zirFromBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9551 const pt = sema.pt;
9552 const zcu = pt.zcu;
9553 const ip = &zcu.intern_pool;
9554
9555 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
9556 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9557 const src = block.nodeOffset(inst_data.src_node);
9558 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9559
9560 // Type has already been validated and layout-resolved by `zirFromBackingIntArgTy`.
9561 const dest_ty = try sema.resolveDestType(block, .unneeded, extra.lhs, .remove_eu_opt, undefined);
9562 dest_ty.assertHasLayout(zcu);
9563
9564 const operand = sema.resolveInst(extra.rhs);
9565 const backing_int_ref = try sema.coerce(block, dest_ty.backingIntType(zcu), operand, operand_src);
9566
9567 if (sema.resolveValue(backing_int_ref)) |backing_int_val| {
9568 if (dest_ty.zigTypeTag(zcu) != .@"enum") {
9569 // we don't do any safety checks for bitpacks
9570 if (backing_int_val.isUndef(zcu)) return pt.undefRef(dest_ty);
9571 return .fromValue(try pt.bitpackValue(dest_ty, backing_int_val));
9572 }
9573 const enum_obj = ip.loadEnumType(dest_ty.toIntern());
9574 if (backing_int_val.isUndef(zcu)) {
9575 if (enum_obj.nonexhaustive) return pt.undefRef(dest_ty);
9576 return sema.failWithUseOfUndef(block, operand_src, null);
9577 }
9578 if (!enum_obj.nonexhaustive and
9579 enum_obj.tagValueIndex(ip, backing_int_val.toIntern()) == null)
9580 {
9581 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
9582 dest_ty.fmt(pt), backing_int_val.fmtValueSema(pt, sema),
9583 });
9584 }
9585 return .fromValue(try pt.enumValue(dest_ty, backing_int_val));
9586 }
9587
9588 switch (dest_ty.classify(zcu)) {
9589 .partially_comptime, .fully_comptime => unreachable, // does not apply to enums or bitpacks
9590 .no_possible_value => unreachable, // backing int also NPV, cannot coerce to NPV type
9591 .one_possible_value => unreachable, // backing int also OPV, should have been resolve above
9592 .runtime => {},
9593 }
9594
9595 if (block.wantSafety()) {
9596 try sema.preparePanicId(src, .invalid_enum_value);
9597 return block.addTyOp(.bit_cast_safe, dest_ty, backing_int_ref);
9598 }
9599 return block.addTyOp(.bit_cast, dest_ty, backing_int_ref);
94119600}
94129601
94139602fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9940,12 +10129,8 @@ fn analyzeSwitchBlock(
994010129 operand_ty.containerLayout(zcu) != .@"packed";
994110130 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
994210131
9943 if (item_ty.zigTypeTag(zcu) == .@"enum" and
9944 validated_switch.seen_enum_fields.len == 0 and
9945 !operand_ty.isNonexhaustiveEnum(zcu))
9946 {
9947 return .void_value; // switch on empty enum/union
9948 }
10132 // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909)
10133 if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated
994910134
995010135 const cond_ref = switch (operand) {
995110136 .simple => |s| s.cond,
......@@ -10451,26 +10636,11 @@ fn finishSwitchBr(
1045110636 }
1045210637
1045310638 var prev_result_overflowed = false;
10454 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
10455 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
10456 .int => .{ item, operand_ty },
10457 .@"enum" => b: {
10458 const int_val: Value = .fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
10459 break :b .{ int_val, int_val.typeOf(zcu) };
10460 },
10461 else => unreachable,
10462 };
10639 while (item.compareScalar(.lte, item_last, item_ty, zcu)) : ({
1046310640 assert(!prev_result_overflowed);
10464 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
10641 const result = try arith.incrementDefinedInt(sema, item_ty, item);
1046510642 prev_result_overflowed = result.overflow;
10466 item = switch (operand_ty.zigTypeTag(zcu)) {
10467 .int => result.val,
10468 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
10469 .ty = operand_ty.toIntern(),
10470 .int = result.val.toIntern(),
10471 } })),
10472 else => unreachable,
10473 };
10643 item = result.val;
1047410644 }) {
1047510645 cases_len += 1;
1047610646 case_block.instructions.clearRetainingCapacity();
......@@ -10603,7 +10773,7 @@ fn finishSwitchBr(
1060310773 break :check_enumerable .{ undefined, min_int };
1060410774 },
1060510775 .@"union", .@"struct" => {
10606 const backing_int_ty = item_ty.bitpackBackingInt(zcu);
10776 const backing_int_ty = item_ty.backingIntType(zcu);
1060710777 const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty);
1060810778 break :check_enumerable .{ undefined, min_backing_int };
1060910779 },
......@@ -10904,7 +11074,7 @@ const ValidatedSwitchBlock = struct {
1090411074 var cur_val = it.next_val orelse return null;
1090511075 const int_ty = switch (type_tag) {
1090611076 .int => item_ty,
10907 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),
11077 .@"union", .@"struct" => item_ty.backingIntType(zcu),
1090811078 else => unreachable,
1090911079 };
1091011080 while (it.next_idx < it.seen_ranges.len and
......@@ -11334,7 +11504,7 @@ fn validateSwitchBlock(
1133411504 check_range: {
1133511505 const int_ty = switch (type_tag) {
1133611506 .int => item_ty,
11337 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),
11507 .@"union", .@"struct" => item_ty.backingIntType(zcu),
1133811508 else => unreachable,
1133911509 };
1134011510 const min_int = try int_ty.minInt(pt, int_ty);
......@@ -11956,14 +12126,7 @@ fn analyzeSwitchCaptures(
1195612126 try sema.analyzeUnreachable(case_block, operand_src, false);
1195712127 break :payload_ref .unreachable_value;
1195812128 };
11959 if (sema.resolveValue(loaded_operand)) |err_val| {
11960 break :payload_ref .fromIntern(try pt.intern(.{ .err = .{
11961 .ty = capture_err_ty.toIntern(),
11962 .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name,
11963 } }));
11964 } else {
11965 break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand);
11966 }
12129 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
1196712130 },
1196812131 .item_refs => |item_refs| {
1196912132 var names: InferredErrorSet.NameMap = .{};
......@@ -11973,14 +12136,7 @@ fn analyzeSwitchCaptures(
1197312136 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});
1197412137 }
1197512138 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());
11976 if (sema.resolveValue(loaded_operand)) |err_val| {
11977 break :payload_ref .fromIntern(try pt.intern(.{ .err = .{
11978 .ty = capture_err_ty.toIntern(),
11979 .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name,
11980 } }));
11981 } else {
11982 break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand);
11983 }
12139 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
1198412140 },
1198512141 }
1198612142 }
......@@ -12461,7 +12617,7 @@ fn validateSwitchItemOrRange(
1246112617 .first = .fromInterned(backing_int_val),
1246212618 .last = .fromInterned(backing_int_val),
1246312619 .src = item_src,
12464 }, item_ty.bitpackBackingInt(zcu), zcu);
12620 }, item_ty.backingIntType(zcu), zcu);
1246512621 },
1246612622 .enum_literal, .@"fn", .pointer, .type => {
1246712623 break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev|
......@@ -18418,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1841818574 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);
1841918575
1842018576 if (union_ty.containerLayout(zcu) == .@"packed") {
18421 return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node));
18577 return sema.bitCastUnchecked(block, union_ty, payload);
1842218578 }
1842318579
1842418580 if (sema.resolveValue(payload)) |payload_val| {
......@@ -18555,7 +18711,7 @@ fn zirStructInit(
1855518711 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
1855618712
1855718713 if (resolved_ty.containerLayout(zcu) == .@"packed") {
18558 const union_val = try sema.bitCast(block, resolved_ty, init_inst, src);
18714 const union_val = try sema.bitCastUnchecked(block, resolved_ty, init_inst);
1855918715 const result_val = try sema.coerce(block, result_ty, union_val, src);
1856018716 if (is_ref) {
1856118717 return sema.analyzeRef(block, src, result_val, .none);
......@@ -19567,14 +19723,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1956719723 operand_ty.fmt(pt),
1956819724 }),
1956919725 };
19570 if (enum_ty.enumFieldCount(zcu) == 0) {
19571 // TODO I don't think this is the correct way to handle this but
19572 // it prevents a crash.
19573 // https://github.com/ziglang/zig/issues/15909
19574 return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{
19575 enum_ty.fmt(pt),
19576 });
19577 }
1957819726 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
1957919727 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
1958019728 const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse {
......@@ -29742,41 +29890,29 @@ fn storePtrVal(
2974229890 }
2974329891}
2974429892
29745fn bitCast(
29893/// Asserts that the layout of `dest_ty` is already resolved.
29894fn bitCastUnchecked(
2974629895 sema: *Sema,
2974729896 block: *Block,
2974829897 dest_ty: Type,
2974929898 inst: Air.Inst.Ref,
29750 inst_src: LazySrcLoc,
2975129899) CompileError!Air.Inst.Ref {
29752 const pt = sema.pt;
29753 const zcu = pt.zcu;
29900 const zcu = sema.pt.zcu;
2975429901 const old_ty = sema.typeOf(inst);
2975529902
2975629903 old_ty.assertHasLayout(zcu);
29757 try sema.ensureLayoutResolved(dest_ty, inst_src, .init);
29904 dest_ty.assertHasLayout(zcu);
2975829905
2975929906 assert(old_ty.hasBitRepresentation(zcu));
2976029907 assert(dest_ty.hasBitRepresentation(zcu));
2976129908 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
2976229909 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
29763
29764 const dest_bits = dest_ty.bitSize(zcu);
29765 const old_bits = old_ty.bitSize(zcu);
29766
29767 if (old_bits != dest_bits) {
29768 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
29769 dest_ty.fmt(pt),
29770 dest_bits,
29771 old_ty.fmt(pt),
29772 old_bits,
29773 });
29774 }
29910 assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu));
2977529911
2977629912 if (sema.resolveValue(inst)) |val| {
2977729913 return .fromValue(try sema.bitCastVal(val, dest_ty));
2977829914 }
29779 try sema.validateRuntimeValue(block, inst_src, inst);
29915
2978029916 return block.addTyOp(.bit_cast, dest_ty, inst);
2978129917}
2978229918
......@@ -29800,6 +29936,26 @@ pub fn bitCastVal(
2980029936 }
2980129937}
2980229938
29939fn errorCastUnchecked(
29940 sema: *Sema,
29941 block: *Block,
29942 dest_ty: Type,
29943 inst: Air.Inst.Ref,
29944) CompileError!Air.Inst.Ref {
29945 const pt = sema.pt;
29946 const zcu = pt.zcu;
29947 assert(dest_ty.zigTypeTag(zcu) == .error_set);
29948 assert(sema.typeOf(inst).zigTypeTag(zcu) == .error_set);
29949 if (sema.resolveValue(inst)) |val| {
29950 if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
29951 return .fromIntern(try pt.intern(.{ .err = .{
29952 .ty = dest_ty.toIntern(),
29953 .name = zcu.intern_pool.indexToKey(val.toIntern()).err.name,
29954 } }));
29955 }
29956 return block.addTyOp(.error_cast, dest_ty, inst);
29957}
29958
2980329959fn checkSpirvSliceAllowed(
2980429960 sema: *Sema,
2980529961 block: *Block,
......@@ -33968,14 +34124,6 @@ fn intFromFloatScalar(
3396834124 return pt.getCoerced(cti_result, int_ty);
3396934125}
3397034126
33971fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
33972 const pt = sema.pt;
33973 if (!int_val.compareAllWithZero(.gte, pt.zcu)) return false;
33974 const end_val = try pt.intValue(tag_ty, end);
33975 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
33976 return true;
33977}
33978
3397934127/// Asserts the type is an exhaustive enum.
3398034128fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
3398134129 const pt = sema.pt;
......@@ -33985,6 +34133,7 @@ fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
3398534133 // The `tagValueIndex` function call below relies on the type being the integer tag type.
3398634134 // `getCoerced` assumes the value will fit the new type.
3398734135 const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type);
34136 if (int_tag_ty.classify(zcu) == .no_possible_value) return false;
3398834137 if (!int.intFitsInType(int_tag_ty, null, zcu)) return false;
3398934138 const int_coerced = try pt.getCoerced(int, int_tag_ty);
3399034139 return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;
src/Sema/reinterpret.zig+4-4
......@@ -353,7 +353,7 @@ const PackValueBytes = struct {
353353 return pt.aggregateValue(ty, elems);
354354 },
355355 .@"packed" => {
356 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
356 const backing_int_val = try pack.primitive(ty.backingIntType(zcu));
357357 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);
358358 return pt.bitpackValue(ty, backing_int_val);
359359 },
......@@ -424,15 +424,15 @@ const PackValueBytes = struct {
424424 }
425425 },
426426 .@"packed" => {
427 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
427 const backing_int_val = try pack.primitive(ty.backingIntType(zcu));
428428 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);
429429 return pt.bitpackValue(ty, backing_int_val);
430430 },
431431 },
432432 .@"enum" => {
433 const tag_int_val = try pack.primitive(ty.intTagType(zcu));
433 const tag_int_val = try pack.primitive(ty.backingIntType(zcu));
434434 if (tag_int_val.isUndef(zcu)) return pt.undefValue(ty);
435 return pt.enumValue(ty, tag_int_val.toIntern());
435 return pt.enumValue(ty, tag_int_val);
436436 },
437437 else => return pack.primitive(ty),
438438 }
src/Sema/type_resolution.zig+23-7
......@@ -63,7 +63,7 @@ pub const LayoutResolveReason = enum {
6363 .@"export" => "for export here",
6464 .@"extern" => "for extern declaration here",
6565 .asm_out_type => "for inline assembly output type declared here",
66 .std_lang_type => "from 'std.lang'",
66 .std_lang_type => "from 'std.lang'",
6767 // zig fmt: on
6868 };
6969 }
......@@ -1325,15 +1325,31 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void {
13251325 const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index);
13261326 break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref);
13271327 };
1328 const empty_exhaustive = enum_obj.field_names.len == 0 and !enum_obj.nonexhaustive;
13281329 const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: {
1329 if (int_tag_ty.zigTypeTag(zcu) != .int) return sema.fail(
1330 &block,
1331 block.src(.container_arg),
1332 "expected integer tag type, found '{f}'",
1333 .{int_tag_ty.fmt(pt)},
1334 );
1330 switch (int_tag_ty.zigTypeTag(zcu)) {
1331 .int => if (empty_exhaustive) return sema.fail(
1332 &block,
1333 block.src(.container_arg),
1334 "empty exhaustive enums must be backed by 'noreturn'",
1335 .{},
1336 ),
1337 .noreturn => if (!empty_exhaustive) return sema.fail(
1338 &block,
1339 block.src(.container_arg),
1340 "non-empty enums cannot be backed by 'noreturn'",
1341 .{},
1342 ),
1343 else => return sema.fail(
1344 &block,
1345 block.src(.container_arg),
1346 "expected integer tag type, found '{f}'",
1347 .{int_tag_ty.fmt(pt)},
1348 ),
1349 }
13351350 break :ty int_tag_ty;
13361351 } else ty: {
1352 if (empty_exhaustive) break :ty .noreturn;
13371353 // Infer the int tag type from the field count
13381354 const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1);
13391355 break :ty try pt.intType(.unsigned, bits);
src/Type.zig+26-20
......@@ -1580,15 +1580,28 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.lang.Type.ContainerLayout
15801580 };
15811581}
15821582
1583pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type {
1583/// Asserts that the type is either an enum or a bitpack.
1584pub fn backingIntType(ty: Type, zcu: *const Zcu) Type {
15841585 const ip = &zcu.intern_pool;
15851586 return switch (ip.indexToKey(ty.toIntern())) {
1587 .enum_type => .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
15861588 .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
15871589 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
15881590 else => unreachable,
15891591 };
15901592}
15911593
1594/// For unions, returns the *backing int* mode, not the *enum tag* mode.
1595pub fn backingIntMode(ty: Type, zcu: *const Zcu) InternPool.BackingTypeMode {
1596 const ip = &zcu.intern_pool;
1597 return switch (ip.indexToKey(ty.toIntern())) {
1598 .enum_type => ip.loadEnumType(ty.toIntern()).int_tag_mode,
1599 .struct_type => ip.loadStructType(ty.toIntern()).packed_backing_mode,
1600 .union_type => ip.loadUnionType(ty.toIntern()).packed_backing_mode,
1601 else => unreachable,
1602 };
1603}
1604
15921605/// Asserts that the type is an error union.
15931606pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
15941607 return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);
......@@ -2093,11 +2106,8 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {
20932106 return try pt.unionValue(ty, tag_val, payload_val);
20942107 } else unreachable;
20952108 },
2096 .enum_type => if (try ty.intTagType(zcu).onePossibleValue(pt)) |int_tag_opv| {
2097 return .fromInterned(try pt.intern(.{ .enum_tag = .{
2098 .ty = ty.toIntern(),
2099 .int = int_tag_opv.toIntern(),
2100 } }));
2109 .enum_type => if (try ty.backingIntType(zcu).onePossibleValue(pt)) |int_tag_opv| {
2110 return try pt.enumValue(ty, int_tag_opv);
21012111 } else null,
21022112
21032113 // values, not types
......@@ -2274,17 +2284,6 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
22742284 return pt.intValue_big(dest_ty, res.toConst());
22752285}
22762286
2277/// Asserts the type is an enum or a union.
2278pub fn intTagType(ty: Type, zcu: *const Zcu) Type {
2279 const ip = &zcu.intern_pool;
2280 const enum_ty: Type = switch (ip.indexToKey(ty.toIntern())) {
2281 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_type),
2282 .enum_type => ty,
2283 else => unreachable,
2284 };
2285 return .fromInterned(ip.loadEnumType(enum_ty.toIntern()).int_tag_type);
2286}
2287
22882287pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {
22892288 const ip = &zcu.intern_pool;
22902289 return switch (ip.indexToKey(ty.toIntern())) {
......@@ -3058,8 +3057,11 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
30583057 .one, .many, .c => .pointer,
30593058 },
30603059
3061 .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode) {
3062 .explicit => null,
3060 .@"enum" => switch (ty.backingIntMode(zcu)) {
3061 .explicit => switch (ty.backingIntType(zcu).toIntern()) {
3062 else => null,
3063 .noreturn_type => .other,
3064 },
30633065 .auto => .{ .enum_inferred_int_tag = ty },
30643066 },
30653067
......@@ -3225,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {
32253227 .float,
32263228 => true,
32273229
3228 .@"enum" => zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode == .explicit,
3230 .@"enum" => {
3231 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
3232 return enum_obj.int_tag_mode == .explicit and
3233 enum_obj.int_tag_type != .noreturn_type;
3234 },
32293235 .pointer, .optional => ty.isPtrAtRuntime(zcu),
32303236 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
32313237
src/Value.zig+11-6
......@@ -146,8 +146,13 @@ pub fn toType(self: Value) Type {
146146 return Type.fromInterned(self.toIntern());
147147}
148148
149pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {
150 return .fromInterned(zcu.intern_pool.indexToKey(val.toIntern()).enum_tag.int);
149/// Asserts that value is defined and of enum or bitpack type.
150pub fn backingInt(val: Value, zcu: *const Zcu) Value {
151 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
152 .enum_tag => |enum_tag| .fromInterned(enum_tag.int),
153 .bitpack => |bitpack| .fromInterned(bitpack.backing_int_val),
154 else => unreachable,
155 };
151156}
152157
153158/// Asserts that `val` is an integer.
......@@ -415,7 +420,7 @@ pub fn writeToPackedMemory(
415420 }
416421 },
417422 .@"enum" => {
418 const int_val = val.intFromEnum(zcu);
423 const int_val = val.backingInt(zcu);
419424 int_val.writeToPackedMemory(zcu, buffer, bit_offset);
420425 },
421426 .int => {
......@@ -564,7 +569,7 @@ pub fn readFromPackedMemory(
564569 return pt.intValue_big(ty, bigint.toConst());
565570 },
566571 .@"enum" => {
567 const int_ty = ty.intTagType(zcu);
572 const int_ty = ty.backingIntType(zcu);
568573 const int_val: Value = try .readFromPackedMemory(int_ty, pt, buffer, bit_offset);
569574 return pt.getCoerced(int_val, ty);
570575 },
......@@ -581,7 +586,7 @@ pub fn readFromPackedMemory(
581586 } })),
582587 .@"struct", .@"union" => {
583588 assert(ty.containerLayout(zcu) == .@"packed");
584 const int_val: Value = try .readFromPackedMemory(ty.bitpackBackingInt(zcu), pt, buffer, bit_offset);
589 const int_val: Value = try .readFromPackedMemory(ty.backingIntType(zcu), pt, buffer, bit_offset);
585590 return pt.bitpackValue(ty, int_val);
586591 },
587592 .array, .vector => {
......@@ -2368,7 +2373,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
23682373 try pt.nullValue(ty),
23692374
23702375 .@"enum" => switch (interpret_mode) {
2371 .direct => try pt.enumValue(ty, (try uninterpret(@intFromEnum(val), ty.intTagType(zcu), pt)).toIntern()),
2376 .direct => try pt.enumValue(ty, try uninterpret(@intFromEnum(val), ty.backingIntType(zcu), pt)),
23722377 .by_name => {
23732378 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls);
23742379 const field_idx = ty.enumFieldIndex(field_name_ip, zcu) orelse return error.TypeMismatch;
src/Zcu/PerThread.zig+16-19
......@@ -3956,7 +3956,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
39563956}
39573957
39583958pub fn intType(pt: Zcu.PerThread, signedness: std.lang.Signedness, bits: u16) Allocator.Error!Type {
3959 return Type.fromInterned(try pt.intern(.{ .int_type = .{
3959 return .fromInterned(try pt.intern(.{ .int_type = .{
39603960 .signedness = signedness,
39613961 .bits = bits,
39623962 } }));
......@@ -3967,15 +3967,15 @@ pub fn errorIntType(pt: Zcu.PerThread) std.mem.Allocator.Error!Type {
39673967}
39683968
39693969pub fn arrayType(pt: Zcu.PerThread, info: InternPool.Key.ArrayType) Allocator.Error!Type {
3970 return Type.fromInterned(try pt.intern(.{ .array_type = info }));
3970 return .fromInterned(try pt.intern(.{ .array_type = info }));
39713971}
39723972
39733973pub fn vectorType(pt: Zcu.PerThread, info: InternPool.Key.VectorType) Allocator.Error!Type {
3974 return Type.fromInterned(try pt.intern(.{ .vector_type = info }));
3974 return .fromInterned(try pt.intern(.{ .vector_type = info }));
39753975}
39763976
39773977pub fn optionalType(pt: Zcu.PerThread, child_type: InternPool.Index) Allocator.Error!Type {
3978 return Type.fromInterned(try pt.intern(.{ .opt_type = child_type }));
3978 return .fromInterned(try pt.intern(.{ .opt_type = child_type }));
39793979}
39803980
39813981pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!Type {
......@@ -3997,7 +3997,7 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!
39973997 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
39983998 }
39993999
4000 return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
4000 return .fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
40014001}
40024002
40034003pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {
......@@ -4037,11 +4037,11 @@ pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Err
40374037/// Use this for `anyframe->T` only.
40384038/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
40394039pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {
4040 return Type.fromInterned(try pt.intern(.{ .anyframe_type = payload_ty.toIntern() }));
4040 return .fromInterned(try pt.intern(.{ .anyframe_type = payload_ty.toIntern() }));
40414041}
40424042
40434043pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {
4044 return Type.fromInterned(try pt.intern(.{ .error_union_type = .{
4044 return .fromInterned(try pt.intern(.{ .error_union_type = .{
40454045 .error_set_type = error_set_ty.toIntern(),
40464046 .payload_type = payload_ty.toIntern(),
40474047 } }));
......@@ -4050,7 +4050,7 @@ pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) A
40504050pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type {
40514051 const names: *const [1]InternPool.NullTerminatedString = &name;
40524052 const comp = pt.zcu.comp;
4053 return Type.fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names));
4053 return .fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names));
40544054}
40554055
40564056/// Sorts `names` in place.
......@@ -4066,7 +4066,7 @@ pub fn errorSetFromUnsortedNames(
40664066 );
40674067 const comp = pt.zcu.comp;
40684068 const new_ty = try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names);
4069 return Type.fromInterned(new_ty);
4069 return .fromInterned(new_ty);
40704070}
40714071
40724072/// Supports only pointers, not pointer-like optionals.
......@@ -4074,7 +4074,7 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
40744074 const zcu = pt.zcu;
40754075 assert(ty.zigTypeTag(zcu) == .pointer and !ty.isSlice(zcu));
40764076 assert(x != 0 or ty.isAllowzeroPtr(zcu));
4077 return Value.fromInterned(try pt.intern(.{ .ptr = .{
4077 return .fromInterned(try pt.intern(.{ .ptr = .{
40784078 .ty = ty.toIntern(),
40794079 .base_addr = .int,
40804080 .byte_offset = x,
......@@ -4082,14 +4082,11 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
40824082}
40834083
40844084/// Creates an enum tag value based on the integer tag value.
4085pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value {
4086 if (std.debug.runtime_safety) {
4087 const tag = ty.zigTypeTag(pt.zcu);
4088 assert(tag == .@"enum");
4089 }
4090 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
4085pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: Value) Allocator.Error!Value {
4086 if (std.debug.runtime_safety) assert(ty.zigTypeTag(pt.zcu) == .@"enum");
4087 return .fromInterned(try pt.intern(.{ .enum_tag = .{
40914088 .ty = ty.toIntern(),
4092 .int = tag_int,
4089 .int = tag_int.toIntern(),
40934090 } }));
40944091}
40954092
......@@ -4103,7 +4100,7 @@ pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Alloca
41034100
41044101 if (enum_type.field_values.len == 0) {
41054102 // Auto-numbered fields.
4106 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
4103 return .fromInterned(try pt.intern(.{ .enum_tag = .{
41074104 .ty = ty.toIntern(),
41084105 .int = try pt.intern(.{ .int = .{
41094106 .ty = enum_type.int_tag_type,
......@@ -4262,7 +4259,7 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value
42624259
42634260/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value.
42644261pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value {
4265 assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.bitpackBackingInt(pt.zcu).toIntern());
4262 assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.backingIntType(pt.zcu).toIntern());
42664263 return .fromInterned(try pt.intern(.{ .bitpack = .{
42674264 .ty = ty.toIntern(),
42684265 .backing_int_val = backing_int_val.toIntern(),
src/codegen.zig+1-1
......@@ -415,7 +415,7 @@ pub fn generateSymbol(
415415 }
416416 },
417417 .enum_tag => |enum_tag| {
418 const int_tag_ty = ty.intTagType(zcu);
418 const int_tag_ty = ty.backingIntType(zcu);
419419 try generateSymbol(bin_file, pt, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent);
420420 },
421421 .float => |float| storage: switch (float.storage) {
src/codegen/aarch64.zig+4-2
......@@ -5,8 +5,10 @@ pub const encoding = @import("aarch64/encoding.zig");
55pub const Mir = @import("aarch64/Mir.zig");
66pub const Select = @import("aarch64/Select.zig");
77
8pub fn legalizeFeatures(_: *const std.Target) ?*Air.Legalize.Features {
9 return null;
8pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
9 return comptime &.initMany(&.{
10 .expand_bit_cast_safe,
11 });
1012}
1113
1214pub fn generate(
src/codegen/aarch64/Select.zig+2
......@@ -362,6 +362,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
362362 air_inst_index = air_body[air_body_index];
363363 continue :air_tag air_tags[@intFromEnum(air_inst_index)];
364364 },
365 .bit_cast_safe => unreachable, // legalized
365366 inline .block, .dbg_inline_block => |air_tag| {
366367 const air_body_block = switch (air_tag) {
367368 else => comptime unreachable,
......@@ -3201,6 +3202,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
32013202 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
32023203 },
32033204 .bit_cast,
3205 .bit_cast_safe, // TODO safety check
32043206 .ptr_cast,
32053207 .ptr_from_int,
32063208 .int_from_ptr,
src/codegen/c.zig+4-2
......@@ -27,6 +27,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
2727 return comptime switch (dev.env.supports(.legalize)) {
2828 inline false, true => |supports_legalize| &.init(.{
2929 // we don't currently ask zig1 to use safe optimization modes
30 .expand_bit_cast_safe = supports_legalize,
3031 .expand_int_cast_safe = supports_legalize,
3132 .expand_int_from_float_safe = supports_legalize,
3233 .expand_int_from_float_optimized_safe = supports_legalize,
......@@ -1460,7 +1461,7 @@ pub const DeclGen = struct {
14601461 }
14611462 return w.writeByte('}');
14621463 },
1463 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1464 .@"packed" => return dg.renderUndefValue(w, ty.backingIntType(zcu), location),
14641465 }
14651466 },
14661467 .tuple_type => |tuple_info| {
......@@ -1520,7 +1521,7 @@ pub const DeclGen = struct {
15201521 if (loaded_union.has_runtime_tag) try w.writeByte(' ');
15211522 if (loaded_union.layout == .auto) try w.writeByte('}');
15221523 },
1523 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1524 .@"packed" => return dg.renderUndefValue(w, ty.backingIntType(zcu), location),
15241525 }
15251526 },
15261527 .error_union_type => |error_union| {
......@@ -2876,6 +2877,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
28762877 .add_safe,
28772878 .sub_safe,
28782879 .mul_safe,
2880 .bit_cast_safe,
28792881 .int_cast_safe,
28802882 .int_from_float_safe,
28812883 .int_from_float_optimized_safe,
src/codegen/c/type.zig+1-2
......@@ -484,8 +484,7 @@ pub const CType = union(enum) {
484484 pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass {
485485 const int_ty: Type = switch (ty.zigTypeTag(zcu)) {
486486 .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu),
487 .@"enum" => ty.intTagType(zcu),
488 .@"struct", .@"union" => ty.bitpackBackingInt(zcu),
487 .@"enum", .@"struct", .@"union" => ty.backingIntType(zcu),
489488 .int => ty,
490489 else => unreachable,
491490 };
src/codegen/c/type/render_defs.zig+2-2
......@@ -212,7 +212,7 @@ pub fn defineComplete(
212212 },
213213 .@"enum" => {
214214 const name_cty: CType = .{ .@"enum" = ty };
215 const cty: CType = try .lower(ty.intTagType(zcu), deps, arena, zcu);
215 const cty: CType = try .lower(ty.backingIntType(zcu), deps, arena, zcu);
216216 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
217217 cty.fmtDeclaratorPrefix(zcu),
218218 name_cty.fmtTypeName(zcu),
......@@ -343,7 +343,7 @@ fn defineBitpack(
343343) (Allocator.Error || Writer.Error)!void {
344344 const zcu = pt.zcu;
345345 const name_cty: CType = .{ .bitpack = ty };
346 const cty: CType = try .lower(ty.bitpackBackingInt(zcu), deps, arena, zcu);
346 const cty: CType = try .lower(ty.backingIntType(zcu), deps, arena, zcu);
347347 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
348348 cty.fmtDeclaratorPrefix(zcu),
349349 name_cty.fmtTypeName(zcu),
src/codegen/llvm.zig+1-1
......@@ -3252,7 +3252,7 @@ pub const Object = struct {
32523252 return ty;
32533253 },
32543254 .opaque_type, .spirv_type => unreachable, // no runtime bits
3255 .enum_type => try o.lowerType(t.intTagType(zcu), repr),
3255 .enum_type => try o.lowerType(t.backingIntType(zcu), repr),
32563256 .func_type => |func_type| try o.lowerFnType(t, func_type),
32573257 .error_set_type, .inferred_error_set_type => try o.errorIntType(repr),
32583258 // values, not types
src/codegen/llvm/FuncGen.zig+24-5
......@@ -465,7 +465,8 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
465465 .alloc => try self.airAlloc(inst),
466466 .ret_ptr => try self.airRetPtr(inst),
467467 .arg => try self.airArg(inst),
468 .bit_cast => try self.airBitCast(inst),
468 .bit_cast => try self.airBitCast(inst, false),
469 .bit_cast_safe => try self.airBitCast(inst, true),
469470 .ptr_cast => try self.airNopCast(inst),
470471 .ptr_from_int => try self.airPtrFromInt(inst),
471472 .int_from_ptr => try self.airIntFromPtr(inst),
......@@ -1254,7 +1255,6 @@ fn cmp(
12541255 const zcu = o.zcu;
12551256 const scalar_ty = operand_ty.scalarType(zcu);
12561257 const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
1257 .@"enum" => scalar_ty.intTagType(zcu),
12581258 .int, .bool, .pointer, .error_set => scalar_ty,
12591259 .optional => blk: {
12601260 const payload_ty = operand_ty.optionalChild(zcu);
......@@ -1328,7 +1328,7 @@ fn cmp(
13281328 return phi.toValue();
13291329 },
13301330 .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),
1331 .@"struct", .@"union" => scalar_ty.bitpackBackingInt(zcu),
1331 .@"enum", .@"struct", .@"union" => scalar_ty.backingIntType(zcu),
13321332 else => unreachable,
13331333 };
13341334 const is_signed = int_ty.isSignedInt(zcu);
......@@ -4539,7 +4539,7 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
45394539 }
45404540}
45414541
4542fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
4542fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value {
45434543 const o = fg.object;
45444544 const zcu = o.zcu;
45454545
......@@ -4564,7 +4564,26 @@ fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
45644564 assert(!isByRef(dest_ty, zcu));
45654565
45664566 const llvm_dest_ty = try o.lowerType(dest_ty, .by_value);
4567 return fg.wip.cast(.bitcast, operand, llvm_dest_ty, "");
4567 const result = try fg.wip.cast(.bitcast, operand, llvm_dest_ty, "");
4568 if (safety and dest_ty.zigTypeTag(zcu) == .@"enum" and !dest_ty.isNonexhaustiveEnum(zcu)) {
4569 const llvm_fn = try o.getIsNamedEnumValueFunction(dest_ty);
4570 const is_valid_enum_val = try fg.wip.call(
4571 .normal,
4572 .fastcc,
4573 .none,
4574 llvm_fn.typeOf(&o.builder),
4575 llvm_fn.toValue(&o.builder),
4576 &.{result},
4577 "",
4578 );
4579 const fail_block = try fg.wip.block(1, "ValidEnumFail");
4580 const ok_block = try fg.wip.block(1, "ValidEnumOk");
4581 _ = try fg.wip.brCond(is_valid_enum_val, ok_block, fail_block, .none);
4582 fg.wip.cursor = .{ .block = fail_block };
4583 try fg.buildSimplePanic(.invalid_enum_value);
4584 fg.wip.cursor = .{ .block = ok_block };
4585 }
4586 return result;
45684587}
45694588
45704589fn airNopCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
src/codegen/riscv64/CodeGen.zig+3-2
......@@ -51,6 +51,7 @@ const InnerError = codegen.Error || error{OutOfRegisters};
5151
5252pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
5353 return comptime &.initMany(&.{
54 .expand_bit_cast_safe,
5455 .expand_int_cast_safe,
5556 .expand_int_from_float_safe,
5657 .expand_int_from_float_optimized_safe,
......@@ -1454,6 +1455,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
14541455 .add_safe,
14551456 .sub_safe,
14561457 .mul_safe,
1458 .bit_cast_safe,
14571459 .int_cast_safe,
14581460 .int_from_float_safe,
14591461 .int_from_float_optimized_safe,
......@@ -5128,7 +5130,6 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
51285130 .@"struct",
51295131 => {
51305132 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
5131 .@"enum" => lhs_ty.intTagType(zcu),
51325133 .int => lhs_ty,
51335134 .bool => .u1,
51345135 .pointer => .u64,
......@@ -5143,7 +5144,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
51435144 return func.fail("TODO riscv cmp non-pointer optionals", .{});
51445145 }
51455146 },
5146 .@"struct", .@"union" => lhs_ty.bitpackBackingInt(zcu),
5147 .@"enum", .@"struct", .@"union" => lhs_ty.backingIntType(zcu),
51475148 else => unreachable,
51485149 };
51495150
src/codegen/sparc64/CodeGen.zig+2-1
......@@ -697,6 +697,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
697697 .add_safe,
698698 .sub_safe,
699699 .mul_safe,
700 .bit_cast_safe,
700701 .int_cast_safe,
701702 .int_from_float_safe,
702703 .int_from_float_optimized_safe,
......@@ -1374,7 +1375,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
13741375
13751376 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
13761377 .vector => unreachable, // Handled by cmp_vector.
1377 .@"enum" => lhs_ty.intTagType(zcu),
1378 .@"enum" => lhs_ty.backingIntType(zcu),
13781379 .int => lhs_ty,
13791380 .bool => .u1,
13801381 .pointer => .usize,
src/codegen/spirv/CodeGen.zig+11-10
......@@ -121,6 +121,7 @@ const StructType = struct {
121121
122122pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
123123 return comptime &.initMany(&.{
124 .expand_bit_cast_safe,
124125 .expand_int_cast_safe,
125126 .expand_int_from_float_safe,
126127 .expand_int_from_float_optimized_safe,
......@@ -1353,7 +1354,7 @@ fn arithmeticTypeInfo(cg: *CodeGen, ty: Type) ArithmeticTypeInfo {
13531354 const target = cg.zcu.getTarget();
13541355 var scalar_ty = ty.scalarType(zcu);
13551356 if (scalar_ty.zigTypeTag(zcu) == .@"enum") {
1356 scalar_ty = scalar_ty.intTagType(zcu);
1357 scalar_ty = scalar_ty.backingIntType(zcu);
13571358 }
13581359 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;
13591360 return switch (scalar_ty.zigTypeTag(zcu)) {
......@@ -1732,8 +1733,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
17321733 return try cg.constructComposite(comp_ty_id, &constituents);
17331734 },
17341735 .enum_tag => {
1735 const int_val = val.intFromEnum(zcu);
1736 const int_ty = ty.intTagType(zcu);
1736 const int_val = val.backingInt(zcu);
1737 const int_ty = ty.backingIntType(zcu);
17371738 break :cache try cg.constant(int_ty, int_val, repr);
17381739 },
17391740 .ptr => return cg.constantPtr(val),
......@@ -2213,7 +2214,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
22132214 const int_info = ty.intInfo(zcu);
22142215 return try cg.intType(int_info.signedness, int_info.bits);
22152216 },
2216 .@"enum" => return try cg.resolveType(ty.intTagType(zcu), repr),
2217 .@"enum" => return try cg.resolveType(ty.backingIntType(zcu), repr),
22172218 .float => {
22182219 const bits = ty.floatBits(target);
22192220 const supported = switch (bits) {
......@@ -5807,7 +5808,7 @@ fn cmp(
58075808 .int, .bool, .float => {},
58085809 .@"enum" => {
58095810 assert(!is_vector);
5810 const ty = lhs.ty.intTagType(zcu);
5811 const ty = lhs.ty.backingIntType(zcu);
58115812 return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty));
58125813 },
58135814 .@"struct" => {
......@@ -6887,7 +6888,7 @@ fn unionInit(
68876888
68886889 const tag_int = if (layout.tag_size != 0) blk: {
68896890 const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field);
6890 const tag_int_val = tag_val.intFromEnum(zcu);
6891 const tag_int_val = tag_val.backingInt(zcu);
68916892 break :blk tag_int_val.toUnsignedInt(zcu);
68926893 } else 0;
68936894
......@@ -8164,7 +8165,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
81648165 break :blk if (backing_bits <= 32) 1 else 2;
81658166 },
81668167 .@"enum" => blk: {
8167 const int_ty = cond_ty.intTagType(zcu);
8168 const int_ty = cond_ty.backingIntType(zcu);
81688169 const int_info = int_ty.intInfo(zcu);
81698170 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);
81708171 if (big_int) return cg.todo("implement composite int switch", .{});
......@@ -8224,7 +8225,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
82248225 const value: Value = .fromInterned(item.toInterned().?);
82258226 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
82268227 .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),
8227 .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu),
8228 .@"enum" => value.backingInt(zcu).toUnsignedInt(zcu),
82288229 .error_set => value.getErrorInt(zcu),
82298230 .pointer => value.toUnsignedInt(zcu),
82308231 else => unreachable,
......@@ -8378,7 +8379,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
83788379 break :blk if (backing_bits <= 32) 1 else 2;
83798380 },
83808381 .@"enum" => blk: {
8381 const int_ty = cond_ty.intTagType(zcu);
8382 const int_ty = cond_ty.backingIntType(zcu);
83828383 const int_info = int_ty.intInfo(zcu);
83838384 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);
83848385 if (big_int) return cg.todo("implement composite int loop switch", .{});
......@@ -8464,7 +8465,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
84648465 const value: Value = .fromInterned(item.toInterned().?);
84658466 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
84668467 .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),
8467 .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu),
8468 .@"enum" => value.backingInt(zcu).toUnsignedInt(zcu),
84688469 .error_set => value.getErrorInt(zcu),
84698470 .pointer => value.toUnsignedInt(zcu),
84708471 else => unreachable,
src/codegen/wasm/CodeGen.zig+9-15
......@@ -32,6 +32,7 @@ const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
3232
3333pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
3434 return comptime &.initMany(&.{
35 .expand_bit_cast_safe,
3536 .expand_int_cast_safe,
3637 .expand_int_from_float_safe,
3738 .expand_int_from_float_optimized_safe,
......@@ -615,7 +616,7 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w
615616 .unrolled => .i32,
616617 },
617618 .@"union", .@"struct" => switch (ty.containerLayout(zcu)) {
618 .@"packed" => typeToValtype(ty.bitpackBackingInt(zcu), zcu, target),
619 .@"packed" => typeToValtype(ty.backingIntType(zcu), zcu, target),
619620 .auto, .@"extern" => .i32,
620621 },
621622 else => .i32, // all represented as reference/immediate
......@@ -1226,7 +1227,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {
12261227 .frame,
12271228 => return ty.hasRuntimeBits(zcu),
12281229 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
1229 .@"packed" => return isByRef(ty.bitpackBackingInt(zcu), zcu, target),
1230 .@"packed" => return isByRef(ty.backingIntType(zcu), zcu, target),
12301231 .@"extern", .auto => return ty.hasRuntimeBits(zcu),
12311232 },
12321233 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,
......@@ -1905,6 +1906,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19051906 .add_safe,
19061907 .sub_safe,
19071908 .mul_safe,
1909 .bit_cast_safe,
19081910 .int_cast_safe,
19091911 .int_from_float_safe,
19101912 .int_from_float_optimized_safe,
......@@ -2525,18 +2527,10 @@ const IntType = struct {
25252527 .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable,
25262528 .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable,
25272529 },
2528 .struct_type => {
2529 const loaded_struct = ip.loadStructType(ty_index);
2530 switch (loaded_struct.layout) {
2531 .auto, .@"extern" => unreachable,
2532 .@"packed" => ty_index = loaded_struct.packed_backing_int_type,
2533 }
2534 },
2535 .union_type => return switch (ip.loadUnionType(ty_index).layout) {
2536 .auto, .@"extern" => unreachable,
2537 .@"packed" => .{ .is_signed = false, .bits = @intCast(ty.bitSize(zcu)) },
2538 },
2539 .enum_type => ty_index = ip.loadEnumType(ty_index).int_tag_type,
2530 .enum_type,
2531 .struct_type,
2532 .union_type,
2533 => ty_index = Type.fromInterned(ty_index).backingIntType(zcu).toIntern(),
25402534 .error_set_type, .inferred_error_set_type => return .{ .is_signed = false, .bits = zcu.errorSetBits() },
25412535 else => unreachable,
25422536 };
......@@ -5134,7 +5128,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
51345128 return .{ .imm32 = 0xaaaaaaaa };
51355129 },
51365130 .@"struct", .@"union" => {
5137 const backing_int_ty = ty.bitpackBackingInt(zcu);
5131 const backing_int_ty = ty.backingIntType(zcu);
51385132 return cg.emitUndefined(backing_int_ty);
51395133 },
51405134 else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}),
src/codegen/x86_64/CodeGen.zig+2
......@@ -63,6 +63,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
6363 .reduce_one_elem_to_bit_cast,
6464 .splat_one_elem_to_bit_cast,
6565
66 .expand_bit_cast_safe,
6667 .expand_int_cast_safe,
6768 .expand_int_from_float_safe,
6869 .expand_int_from_float_optimized_safe,
......@@ -67446,6 +67447,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
6744667447 .int_from_error,
6744767448 .union_from_enum,
6744867449 => try cg.airBitCast(inst),
67450 .bit_cast_safe => unreachable,
6744967451 .block => {
6745067452 const block = cg.air.unwrapBlock(inst);
6745167453 if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none);
src/link/Dwarf.zig+1
......@@ -5098,6 +5098,7 @@ fn DeclValEnum(comptime T: type) type {
50985098 if (min_value == null or min_value.? > value) min_value = value;
50995099 if (max_value == null or max_value.? < value) max_value = value;
51005100 }
5101 if (fields_len == 0) return enum {};
51015102 const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0);
51025103 var field_vals: [fields_len]TagInt = undefined;
51035104 for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name);
src/link/Wasm/Flush.zig+1-1
......@@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
151151 assert(ip.indexToKey(data.ip_index) == .enum_type);
152152 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
153153 if (!gop.found_existing) {
154 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
154 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).backingIntType(zcu);
155155 gop.value_ptr.* = .{ .tag_name = .{
156156 .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}),
157157 .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target),
src/print_value.zig+18-12
......@@ -94,15 +94,14 @@ pub fn print(
9494 enum_literal.fmt(ip),
9595 }),
9696 .enum_tag => |enum_tag| {
97 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());
98 if (enum_type.tagValueIndex(ip, enum_tag.int)) |tag_index| {
99 return writer.print(".{f}", .{enum_type.field_names.get(ip)[tag_index].fmt(ip)});
97 const ty: Type = .fromInterned(enum_tag.ty);
98 const enum_obj = ip.loadEnumType(ty.toIntern());
99 if (enum_obj.tagValueIndex(ip, enum_tag.int)) |tag_index| {
100 return writer.print(".{f}", .{enum_obj.field_names.get(ip)[tag_index].fmt(ip)});
100101 }
101 if (level == 0) {
102 return writer.writeAll("@enumFromInt(...)");
103 }
104 try writer.writeAll("@enumFromInt(");
105 try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
102 try writer.writeAll("@fromBackingInt(");
103 if (level == 0) return writer.writeAll("...)");
104 try print(.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
106105 try writer.writeAll(")");
107106 },
108107 .float => |float| switch (float.storage) {
......@@ -190,10 +189,17 @@ pub fn print(
190189 try writer.writeAll(" }");
191190 return;
192191 },
193 .@"union" => {
194 try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)});
195 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
196 try writer.writeAll("))");
192 .@"union" => switch (ty.backingIntMode(zcu)) {
193 .auto => {
194 try writer.print("@bitCast(@as({f}, ", .{ty.backingIntType(zcu).fmt(pt)});
195 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
196 try writer.writeAll("))");
197 },
198 .explicit => {
199 try writer.writeAll("@fromBackingInt(");
200 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
201 try writer.writeAll(")");
202 },
197203 },
198204 else => unreachable,
199205 }
src/print_zir.zig+14
......@@ -193,6 +193,7 @@ const Writer = struct {
193193 .elem_type,
194194 .indexable_ptr_elem_type,
195195 .splat_op_result_ty,
196 .from_backing_int_arg_ty,
196197 .indexable_ptr_len,
197198 .anyframe_type,
198199 .bit_not,
......@@ -232,6 +233,7 @@ const Writer = struct {
232233 .compile_error,
233234 .set_eval_branch_quota,
234235 .int_from_enum,
236 .backing_int,
235237 .align_of,
236238 .int_from_bool,
237239 .embed_file,
......@@ -418,6 +420,8 @@ const Writer = struct {
418420
419421 .for_len => try self.writePlNodeMultiOp(stream, inst),
420422
423 .from_backing_int => try self.writePlNodeBin(stream, inst),
424
421425 .elem_val_imm => try self.writeElemValImm(stream, inst),
422426
423427 .@"export" => try self.writePlNodeExport(stream, inst),
......@@ -985,6 +989,16 @@ const Writer = struct {
985989 try self.writeSrcNode(stream, inst_data.src_node);
986990 }
987991
992 fn writeFromBackingInt(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
993 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
994 const extra = self.code.extraData(Zir.Inst.FromBackingInt, inst_data.payload_index);
995 try self.writeInstRef(stream, extra.data.result_type);
996 try stream.writeAll(", ");
997 try self.writeBracedBody(stream, self.code.bodySlice(extra.end, extra.data.body_len));
998 try stream.writeAll(") ");
999 try self.writeSrcNode(stream, inst_data.src_node);
1000 }
1001
9881002 fn writeBuiltinCall(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
9891003 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
9901004 const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior.zig+1
......@@ -6,6 +6,7 @@ test {
66 _ = @import("behavior/alignof.zig");
77 _ = @import("behavior/array.zig");
88 _ = @import("behavior/atomics.zig");
9 _ = @import("behavior/backing_int.zig");
910 _ = @import("behavior/basic.zig");
1011 _ = @import("behavior/bit_shifting.zig");
1112 _ = @import("behavior/bitcast.zig");
test/behavior/backing_int.zig created+384
......@@ -0,0 +1,384 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const expect = std.testing.expect;
4const assert = std.debug.assert;
5
6const E1 = enum(u8) {
7 a,
8 b,
9 c,
10 d,
11 const expected = .{
12 .val = @as(E1, .b),
13 .int = @as(@typeInfo(E1).@"enum".tag_type, 1),
14 };
15};
16const E2 = enum(i20) {
17 x,
18 y,
19 z = -5,
20 const expected = .{
21 .val = @as(E2, .z),
22 .int = @as(@typeInfo(E2).@"enum".tag_type, -5),
23 };
24};
25const E3 = enum(i32) {
26 _,
27 const zero: E3 = @bitCast(@as(i32, 0));
28 const expected = .{
29 .val = @as(E3, .zero),
30 .int = @as(@typeInfo(E3).@"enum".tag_type, 0),
31 };
32};
33const E4 = enum(i200) {
34 min = -(1 << 199),
35 const expected = .{
36 .val = @as(E4, .min),
37 .int = @as(@typeInfo(E4).@"enum".tag_type, -(1 << 199)),
38 };
39};
40const E5 = enum(u0) {
41 a,
42 const expected = .{
43 .val = @as(E5, .a),
44 .int = @as(@typeInfo(E5).@"enum".tag_type, 0),
45 };
46};
47
48test "@backingInt with enums" {
49 const static = struct {
50 fn doTheTest(v1: E1, v2: E2, v3: E3, v4: E4, v5: E5) !void {
51 const b1 = @backingInt(v1);
52 comptime assert(@TypeOf(b1) == @typeInfo(E1).@"enum".tag_type);
53 try expect(b1 == E1.expected.int);
54
55 const b2 = @backingInt(v2);
56 comptime assert(@TypeOf(b2) == @typeInfo(E2).@"enum".tag_type);
57 try expect(b2 == E2.expected.int);
58
59 const b3 = @backingInt(v3);
60 comptime assert(@TypeOf(b3) == @typeInfo(E3).@"enum".tag_type);
61 try expect(b3 == E3.expected.int);
62
63 const b4 = @backingInt(v4);
64 comptime assert(@TypeOf(b4) == @typeInfo(E4).@"enum".tag_type);
65 try expect(b4 == E4.expected.int);
66
67 const b5 = @backingInt(v5);
68 comptime assert(@TypeOf(b5) == @typeInfo(E5).@"enum".tag_type);
69 try expect(b5 == E5.expected.int);
70 }
71 };
72 try static.doTheTest(E1.expected.val, E2.expected.val, E3.expected.val, E4.expected.val, E5.expected.val);
73 try comptime static.doTheTest(E1.expected.val, E2.expected.val, E3.expected.val, E4.expected.val, E5.expected.val);
74}
75
76test "@fromBackingInt with enums" {
77 const static = struct {
78 fn doTheTest(
79 b1: @typeInfo(E1).@"enum".tag_type,
80 b2: @typeInfo(E2).@"enum".tag_type,
81 b3: @typeInfo(E3).@"enum".tag_type,
82 b4: @typeInfo(E4).@"enum".tag_type,
83 b5: @typeInfo(E5).@"enum".tag_type,
84 ) !void {
85 const v1: E1 = @fromBackingInt(b1);
86 try expect(v1 == E1.expected.val);
87
88 const v2: E2 = @fromBackingInt(b2);
89 try expect(v2 == E2.expected.val);
90
91 const v3: E3 = @fromBackingInt(b3);
92 try expect(v3 == E3.expected.val);
93
94 const v4: E4 = @fromBackingInt(b4);
95 try expect(v4 == E4.expected.val);
96
97 const v5: E5 = @fromBackingInt(b5);
98 try expect(v5 == E5.expected.val);
99 }
100 };
101 try static.doTheTest(E1.expected.int, E2.expected.int, E3.expected.int, E4.expected.int, E5.expected.int);
102 try comptime static.doTheTest(E1.expected.int, E2.expected.int, E3.expected.int, E4.expected.int, E5.expected.int);
103}
104
105const T1 = union(E1) {
106 a: u8,
107 b: []const u16,
108 c: []const u8,
109 d: i32,
110 const expected = .{
111 .val = @unionInit(T1, @tagName(E1.expected.val), &.{ 1, 2, 3 }),
112 .int = E1.expected.int,
113 };
114};
115const T2 = union(E2) {
116 x,
117 y: i32,
118 z,
119 const expected = .{
120 .val = @unionInit(T2, @tagName(E2.expected.val), {}),
121 .int = E2.expected.int,
122 };
123};
124const T4 = union(E4) {
125 min: f32,
126 const expected = .{
127 .val = @unionInit(T4, @tagName(E4.expected.val), 0.123),
128 .int = E4.expected.int,
129 };
130};
131const T5 = union(E5) {
132 a: u0,
133 const expected = .{
134 .val = @unionInit(T5, @tagName(E5.expected.val), 0),
135 .int = E5.expected.int,
136 };
137};
138
139test "@backingInt with tagged unions" {
140 const static = struct {
141 fn doTheTest(v1: T1, v2: T2, v4: T4, v5: T5) !void {
142 const b1 = @backingInt(v1);
143 comptime assert(@TypeOf(b1) == @typeInfo(@typeInfo(T1).@"union".tag_type.?).@"enum".tag_type);
144 try expect(b1 == E1.expected.int);
145
146 const b2 = @backingInt(v2);
147 comptime assert(@TypeOf(b2) == @typeInfo(@typeInfo(T2).@"union".tag_type.?).@"enum".tag_type);
148 try expect(b2 == E2.expected.int);
149
150 const b4 = @backingInt(v4);
151 comptime assert(@TypeOf(b4) == @typeInfo(@typeInfo(T4).@"union".tag_type.?).@"enum".tag_type);
152 try expect(b4 == E4.expected.int);
153
154 const b5 = @backingInt(v5);
155 comptime assert(@TypeOf(b5) == @typeInfo(@typeInfo(T5).@"union".tag_type.?).@"enum".tag_type);
156 try expect(b5 == E5.expected.int);
157 }
158 };
159 try static.doTheTest(T1.expected.val, T2.expected.val, T4.expected.val, T5.expected.val);
160 try comptime static.doTheTest(T1.expected.val, T2.expected.val, T4.expected.val, T5.expected.val);
161}
162
163const S1 = packed struct(u8) {
164 a: u4,
165 b: i4,
166 const expected = .{
167 .val = @as(S1, .{ .a = 0b1000, .b = 0b0010 }),
168 .int = @as(@typeInfo(S1).@"struct".backing_integer.?, 0b0010_1000),
169 };
170};
171const S2 = packed struct(i20) {
172 a: u10,
173 b: enum(i10) { x, y, z },
174 const expected = .{
175 .val = @as(S2, .{ .a = 0b0011001100, .b = .z }),
176 .int = @as(@typeInfo(S2).@"struct".backing_integer.?, 0b0000000010_0011001100),
177 };
178};
179const S3 = packed struct(i32) {
180 a: packed struct(u12) {
181 x: u8,
182 y: i4,
183 },
184 b: packed union(i20) {
185 x: i20,
186 y: enum(u20) { u, v },
187 },
188 const expected = .{
189 .val = @as(S3, .{ .a = .{ .x = 0b10010001, .y = 0b0110 }, .b = .{ .y = .v } }),
190 .int = @as(@typeInfo(S3).@"struct".backing_integer.?, 0b00000000000000000001_0110_10010001),
191 };
192};
193const S4 = packed struct(i200) {
194 a: u200,
195 const expected = .{
196 .val = @as(S4, .{ .a = (1 << 199) + 10 }),
197 .int = @as(@typeInfo(S4).@"struct".backing_integer.?, @bitCast(@as(u200, (1 << 199) + 10))),
198 };
199};
200const S5 = packed struct(u0) {
201 a: u0,
202 const expected = .{
203 .val = @as(S5, .{ .a = 0 }),
204 .int = @as(@typeInfo(S5).@"struct".backing_integer.?, 0),
205 };
206};
207
208test "@backingInt with packed structs" {
209 const static = struct {
210 fn doTheTest(v1: S1, v2: S2, v3: S3, v4: S4, v5: S5) !void {
211 const b1 = @backingInt(v1);
212 comptime assert(@TypeOf(b1) == @typeInfo(S1).@"struct".backing_integer.?);
213 try expect(b1 == S1.expected.int);
214
215 const b2 = @backingInt(v2);
216 comptime assert(@TypeOf(b2) == @typeInfo(S2).@"struct".backing_integer.?);
217 try expect(b2 == S2.expected.int);
218
219 const b3 = @backingInt(v3);
220 comptime assert(@TypeOf(b3) == @typeInfo(S3).@"struct".backing_integer.?);
221 try expect(b3 == S3.expected.int);
222
223 const b4 = @backingInt(v4);
224 comptime assert(@TypeOf(b4) == @typeInfo(S4).@"struct".backing_integer.?);
225 try expect(b4 == S4.expected.int);
226
227 const b5 = @backingInt(v5);
228 comptime assert(@TypeOf(b5) == @typeInfo(S5).@"struct".backing_integer.?);
229 try expect(b5 == S5.expected.int);
230 }
231 };
232 try static.doTheTest(S1.expected.val, S2.expected.val, S3.expected.val, S4.expected.val, S5.expected.val);
233 try comptime static.doTheTest(S1.expected.val, S2.expected.val, S3.expected.val, S4.expected.val, S5.expected.val);
234}
235
236test "@fromBackingInt with packed structs" {
237 const static = struct {
238 fn doTheTest(
239 b1: @typeInfo(S1).@"struct".backing_integer.?,
240 b2: @typeInfo(S2).@"struct".backing_integer.?,
241 b3: @typeInfo(S3).@"struct".backing_integer.?,
242 b4: @typeInfo(S4).@"struct".backing_integer.?,
243 b5: @typeInfo(S5).@"struct".backing_integer.?,
244 ) !void {
245 const v1: S1 = @fromBackingInt(b1);
246 try expect(v1 == S1.expected.val);
247
248 const v2: S2 = @fromBackingInt(b2);
249 try expect(v2 == S2.expected.val);
250
251 const v3: S3 = @fromBackingInt(b3);
252 try expect(v3 == S3.expected.val);
253
254 // https://codeberg.org/ziglang/zig/issues/35982
255 if (builtin.zig_backend != .stage2_x86_64) {
256 const v4: S4 = @fromBackingInt(b4);
257 try expect(v4 == S4.expected.val);
258 }
259
260 const v5: S5 = @fromBackingInt(b5);
261 try expect(v5 == S5.expected.val);
262 }
263 };
264 try static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int);
265 try comptime static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int);
266}
267
268const U1 = packed union(u8) {
269 a: u8,
270 b: i8,
271 const expected = .{
272 .val = @as(U1, .{ .b = -123 }),
273 .int = @as(@typeInfo(U1).@"union".backing_integer.?, @bitCast(@as(i8, -123))),
274 };
275};
276const U2 = packed union(i20) {
277 a: u20,
278 b: enum(i20) { x, y, z },
279 const expected = .{
280 .val = @as(U2, .{ .b = .z }),
281 .int = @as(@typeInfo(U2).@"union".backing_integer.?, 0b00000000000000000000000000000010),
282 };
283};
284const U3 = packed union(i32) {
285 a: packed struct(u32) {
286 x: u18,
287 y: i14,
288 },
289 b: packed union(u32) {
290 x: i32,
291 y: enum(u32) { u, v },
292 },
293 const expected = .{
294 .val = @as(U3, .{ .b = .{ .y = .v } }),
295 .int = @as(@typeInfo(U3).@"union".backing_integer.?, 0b00000000000000000000000000000001),
296 };
297};
298const U4 = packed union(i200) {
299 a: u200,
300 const expected = .{
301 .val = @as(U4, .{ .a = (1 << 199) + 10 }),
302 .int = @as(@typeInfo(U4).@"union".backing_integer.?, @bitCast(@as(u200, (1 << 199) + 10))),
303 };
304};
305const U5 = packed union(u0) {
306 a: u0,
307 const expected = .{
308 .val = @as(U5, .{ .a = 0 }),
309 .int = @as(@typeInfo(U5).@"union".backing_integer.?, 0),
310 };
311};
312
313test "@backingInt with packed unions" {
314 const static = struct {
315 fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void {
316 const b1 = @backingInt(v1);
317 comptime assert(@TypeOf(b1) == @typeInfo(U1).@"union".backing_integer.?);
318 try expect(b1 == U1.expected.int);
319
320 const b2 = @backingInt(v2);
321 comptime assert(@TypeOf(b2) == @typeInfo(U2).@"union".backing_integer.?);
322 try expect(b2 == U2.expected.int);
323
324 const b3 = @backingInt(v3);
325 comptime assert(@TypeOf(b3) == @typeInfo(U3).@"union".backing_integer.?);
326 try expect(b3 == U3.expected.int);
327
328 const b4 = @backingInt(v4);
329 comptime assert(@TypeOf(b4) == @typeInfo(U4).@"union".backing_integer.?);
330 try expect(b4 == U4.expected.int);
331
332 const b5 = @backingInt(v5);
333 comptime assert(@TypeOf(b5) == @typeInfo(U5).@"union".backing_integer.?);
334 try expect(b5 == U5.expected.int);
335 }
336 };
337 try static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val);
338 try comptime static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val);
339}
340
341test "@fromBackingInt with packed unions" {
342 const static = struct {
343 fn doTheTest(
344 b1: @typeInfo(U1).@"union".backing_integer.?,
345 b2: @typeInfo(U2).@"union".backing_integer.?,
346 b3: @typeInfo(U3).@"union".backing_integer.?,
347 b4: @typeInfo(U4).@"union".backing_integer.?,
348 b5: @typeInfo(U5).@"union".backing_integer.?,
349 ) !void {
350 const v1: U1 = @fromBackingInt(b1);
351 try expect(v1 == U1.expected.val);
352
353 const v2: U2 = @fromBackingInt(b2);
354 try expect(v2 == U2.expected.val);
355
356 const v3: U3 = @fromBackingInt(b3);
357 try expect(v3 == U3.expected.val);
358
359 // https://codeberg.org/ziglang/zig/issues/35982
360 if (builtin.zig_backend != .stage2_x86_64) {
361 const v4: U4 = @fromBackingInt(b4);
362 try expect(v4 == U4.expected.val);
363 }
364
365 const v5: U5 = @fromBackingInt(b5);
366 try expect(v5 == U5.expected.val);
367 }
368 };
369 try static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int);
370 try comptime static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int);
371}
372
373test "@fromBackingInt provides result type to its argument" {
374 const E = enum(u32) { a, b, c };
375 const static = struct {
376 fn doTheTest(x: u8) !void {
377 const e: E = @fromBackingInt(x);
378 try expect(e == .b);
379 try expect(@backingInt(e) == x);
380 }
381 };
382 try static.doTheTest(1);
383 try comptime static.doTheTest(1);
384}
test/behavior/enum.zig+15-23
......@@ -1451,29 +1451,6 @@ test "comptime @enumFromInt with signed arithmetic" {
14511451 comptime assert(@intFromEnum(x) == 0);
14521452}
14531453
1454test "switch on empty enum" {
1455 const E = enum {};
1456 var e: E = undefined;
1457 _ = &e;
1458 switch (e) {}
1459}
1460
1461test "switch on empty enum with a specified tag type" {
1462 const E = enum(u8) {};
1463 var e: E = undefined;
1464 _ = &e;
1465 switch (e) {}
1466}
1467
1468test "empty enum passed as argument" {
1469 const E = enum {
1470 fn f(e: @This()) void {
1471 switch (e) {}
1472 }
1473 };
1474 E.f(@as(E, undefined));
1475}
1476
14771454test "enum int tag type uses declaration inside the enum" {
14781455 const static = struct {
14791456 const E = enum(E.IntTag) {
......@@ -1488,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" {
14881465 try expect(val == .b);
14891466 try expect(@intFromEnum(val) == 1);
14901467}
1468
1469test "convert from/to backing int" {
1470 const E = enum(u33) {
1471 a,
1472 b,
1473 c,
1474 fn doTheTest(s: @This()) !void {
1475 const backing_int = @backingInt(s);
1476 const reconstructed: @This() = @fromBackingInt(backing_int);
1477 try expect(reconstructed == s);
1478 }
1479 };
1480 try E.doTheTest(.b);
1481 try comptime E.doTheTest(.b);
1482}
test/behavior/packed-struct.zig+36-8
......@@ -122,7 +122,7 @@ test "correct sizeOf and offsets in packed structs" {
122122 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
123123 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
124124
125 const PStruct = packed struct {
125 const PStruct = packed struct(u32) {
126126 bool_a: bool,
127127 bool_b: bool,
128128 bool_c: bool,
......@@ -162,7 +162,7 @@ test "correct sizeOf and offsets in packed structs" {
162162 try expectEqual(22, @bitOffsetOf(PStruct, "u10_b"));
163163 try expectEqual(4, @sizeOf(PStruct));
164164
165 const s1 = @as(PStruct, @bitCast(@as(u32, 0x12345678)));
165 const s1: PStruct = @fromBackingInt(0x12345678);
166166 try expectEqual(false, s1.bool_a);
167167 try expectEqual(false, s1.bool_b);
168168 try expectEqual(false, s1.bool_c);
......@@ -176,7 +176,7 @@ test "correct sizeOf and offsets in packed structs" {
176176 try expectEqual(0b1101000101, s1.u10_a);
177177 try expectEqual(0b0001001000, s1.u10_b);
178178
179 const s2 = @as(packed struct { x: u1, y: u7, z: u24 }, @bitCast(@as(u32, 0xd5c71ff4)));
179 const s2: packed struct(u32) { x: u1, y: u7, z: u24 } = @fromBackingInt(0xd5c71ff4);
180180 try expectEqual(0, s2.x);
181181 try expectEqual(0b1111010, s2.y);
182182 try expectEqual(0xd5c71f, s2.z);
......@@ -191,7 +191,7 @@ test "nested packed structs" {
191191 const S2 = packed struct { d: u8, e: u8, f: u8 };
192192
193193 const S3 = packed struct { x: S1, y: S2 };
194 const S3Padded = packed struct { s3: S3, pad: u16 };
194 const S3Padded = packed struct(u64) { s3: S3, pad: u16 };
195195
196196 try expectEqual(48, @bitSizeOf(S3));
197197 try expectEqual(@sizeOf(u48), @sizeOf(S3));
......@@ -199,7 +199,7 @@ test "nested packed structs" {
199199 try expectEqual(3, @offsetOf(S3, "y"));
200200 try expectEqual(24, @bitOffsetOf(S3, "y"));
201201
202 const s3 = @as(S3Padded, @bitCast(@as(u64, 0xe952d5c71ff4))).s3;
202 const s3 = @as(S3Padded, @fromBackingInt(0xe952d5c71ff4)).s3;
203203 try expectEqual(0xf4, s3.x.a);
204204 try expectEqual(0x1f, s3.x.b);
205205 try expectEqual(0xc7, s3.x.c);
......@@ -558,7 +558,7 @@ test "packed struct fields modification" {
558558 // Originally reported at https://github.com/ziglang/zig/issues/16615
559559 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
560560
561 const Small = packed struct {
561 const Small = packed struct(u16) {
562562 val: u8 = 0,
563563 lo: u4 = 0,
564564 hi: u4 = 0,
......@@ -570,12 +570,12 @@ test "packed struct fields modification" {
570570 .lo = 3,
571571 .hi = 4,
572572 };
573 try expect(@as(u16, @bitCast(Small.p)) == 0x4312);
573 try expect(@backingInt(Small.p) == 0x4312);
574574
575575 Small.p.val -= Small.p.lo;
576576 Small.p.val += Small.p.hi;
577577 Small.p.hi -= Small.p.lo;
578 try expect(@as(u16, @bitCast(Small.p)) == 0x1313);
578 try expect(@backingInt(Small.p) == 0x1313);
579579}
580580
581581test "nested packed struct field access test" {
......@@ -1246,3 +1246,31 @@ test "initialize packed struct field to undefined at comptime" {
12461246 const val: S = .{ .x = undefined };
12471247 _ = val;
12481248}
1249
1250test "convert from/to backing int" {
1251 const S = packed struct(u33) {
1252 a: u7,
1253 b: enum(u10) { x, y, z },
1254 c: f16,
1255 fn doTheTest(s: @This()) !void {
1256 const backing_int = @backingInt(s);
1257 const reconstructed: @This() = @fromBackingInt(backing_int);
1258 try expect(reconstructed == s);
1259 }
1260 };
1261 try S.doTheTest(.{ .a = 123, .b = .y, .c = 0.23 });
1262 try comptime S.doTheTest(.{ .a = 123, .b = .y, .c = 0.23 });
1263}
1264
1265test "equality with wide backing integer" {
1266 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982
1267
1268 const S = packed struct(i200) {
1269 x: u200,
1270 fn doTheTest(s: @This(), int: i200) !void {
1271 try expect(s == @as(@This(), @bitCast(int)));
1272 }
1273 };
1274 try S.doTheTest(.{ .x = (1 << 200) - 1 }, -1);
1275 try comptime S.doTheTest(.{ .x = (1 << 200) - 1 }, -1);
1276}
test/behavior/packed-union.zig+27
......@@ -227,3 +227,30 @@ test "initialize packed union field to undefined at comptime" {
227227 const val: U = .{ .x = undefined };
228228 _ = val;
229229}
230
231test "convert from/to backing int" {
232 const U = packed union(u10) {
233 a: i10,
234 b: enum(u10) { x, y, z },
235 fn doTheTest(u: @This()) !void {
236 const backing_int = @backingInt(u);
237 const reconstructed: @This() = @fromBackingInt(backing_int);
238 try expect(reconstructed == u);
239 }
240 };
241 try U.doTheTest(.{ .a = 123 });
242 try comptime U.doTheTest(.{ .a = 123 });
243}
244
245test "equality with wide backing integer" {
246 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982
247
248 const U = packed union(i200) {
249 x: u200,
250 fn doTheTest(s: @This(), int: i200) !void {
251 try expect(s == @as(@This(), @bitCast(int)));
252 }
253 };
254 try U.doTheTest(.{ .x = (1 << 200) - 1 }, -1);
255 try comptime U.doTheTest(.{ .x = (1 << 200) - 1 }, -1);
256}
test/behavior/switch.zig+2-2
......@@ -1132,13 +1132,13 @@ test "decl literals as switch cases" {
11321132 try comptime E.doTheTest(.foo);
11331133}
11341134
1135// TODO audit after #15909 and/or #19855 are decided/implemented.
1135// TODO audit after https://github.com/ziglang/zig/issues/15909 is fully decided.
11361136// When we do that, consider adding an 'error{}' case if possible.
11371137test "switch with uninstantiable union fields" {
11381138 const U = union(enum) {
11391139 ok: void,
11401140 a: noreturn,
1141 b: noreturn,
1141 b: enum {},
11421142
11431143 fn doTheTest(u: @This()) void {
11441144 switch (u) {
test/behavior/type.zig+11-3
......@@ -266,13 +266,21 @@ test "Type.Union from regular enum" {
266266test "Type.Union from empty regular enum" {
267267 const E = enum {};
268268 const U = @Union(.auto, E, &.{}, &.{}, &.{});
269 try testing.expectEqual(@typeInfo(U).@"union".field_names.len, 0);
269
270 const info = @typeInfo(U).@"union";
271 try testing.expect(info.field_names.len == 0);
272 try testing.expect(info.tag_type != null);
273 try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn);
270274}
271275
272276test "Type.Union from empty Type.Enum" {
273 const E = @Enum(u0, .exhaustive, &.{}, &.{});
277 const E = @Enum(noreturn, .exhaustive, &.{}, &.{});
274278 const U = @Union(.auto, E, &.{}, &.{}, &.{});
275 try testing.expectEqual(@typeInfo(U).@"union".field_names.len, 0);
279
280 const info = @typeInfo(U).@"union";
281 try testing.expect(info.field_names.len == 0);
282 try testing.expect(info.tag_type != null);
283 try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn);
276284}
277285
278286test "Type.Fn" {
test/cases/compile_errors/backing_int_invalid_arg_type.zig created+38
......@@ -0,0 +1,38 @@
1const S1 = extern struct { x: u32 };
2export fn entry1(x: S1) u32 {
3 return @backingInt(x);
4}
5
6const U1 = extern union { x: u32 };
7export fn entry2(x: U1) u32 {
8 return @backingInt(x);
9}
10
11export fn entry3(x: u32) u32 {
12 return @backingInt(x);
13}
14
15const S2 = packed struct { x: u32 };
16export fn entry4(x: u32) u32 {
17 return @backingInt(@as(S2, .{ .x = x }));
18}
19
20const U2 = packed union { x: u32 };
21export fn entry5(x: u32) u32 {
22 return @backingInt(@as(U2, .{ .x = x }));
23}
24
25// error
26//
27// :3:24: error: non-packed struct 'tmp.S1' does not have a backing integer
28// :1:19: note: struct declared here
29// :8:24: error: non-packed union 'tmp.U1' does not have a backing integer
30// :8:24: note: untagged union 'tmp.U1' does not have an enum tag with a backing integer
31// :6:19: note: union declared here
32// :12:24: error: expected enum, tagged union, packed union or packed struct, found 'u32'
33// :17:24: error: @backingInt is ambiguous for type 'tmp.S2'
34// :15:19: note: backing integer type of struct is inferred
35// :15:19: note: consider explicitly specifying the backing integer type
36// :22:24: error: @backingInt is ambiguous for type 'tmp.U2'
37// :20:19: note: backing integer type of union is inferred
38// :20:19: note: consider explicitly specifying the backing integer type
test/cases/compile_errors/bitCast_with_invalid_array_element_type.zig deleted-23
......@@ -1,23 +0,0 @@
1export fn foo() void {
2 const S = struct {
3 f: u8,
4 };
5 _ = @as([@sizeOf(S)]u8, @bitCast([1]S{undefined}));
6}
7
8export fn bar() void {
9 const S = struct {
10 f: u8,
11 };
12 _ = @as([1]S, @bitCast(@as([@sizeOf(S)]u8, undefined)));
13}
14
15export fn baz() void {
16 _ = @as([1]u32, @bitCast([1]comptime_int{0}));
17}
18
19// error
20//
21// :5:42: error: cannot @bitCast from '[1]tmp.foo.S'
22// :12:19: error: cannot @bitCast to '[1]tmp.bar.S'
23// :16:45: error: cannot @bitCast from '[1]comptime_int'
test/cases/compile_errors/bitcast_invalid_types.zig created+205
......@@ -0,0 +1,205 @@
1const y: u32 = 0;
2
3export fn entry1() void {
4 _ = @as(comptime_float, @bitCast(y));
5}
6export fn entry2() void {
7 const x: comptime_float = undefined;
8 _ = @as(u32, @bitCast(x));
9}
10
11export fn entry3() void {
12 _ = @as(comptime_int, @bitCast(y));
13}
14export fn entry4() void {
15 const x: comptime_int = undefined;
16 _ = @as(u32, @bitCast(x));
17}
18
19export fn entry5() void {
20 _ = @as(@EnumLiteral(), @bitCast(y));
21}
22export fn entry6() void {
23 const x: @EnumLiteral() = undefined;
24 _ = @as(u32, @bitCast(x));
25}
26
27export fn entry7() void {
28 _ = @as(error{}, @bitCast(y));
29}
30export fn entry8() void {
31 const x: error{} = undefined;
32 _ = @as(u32, @bitCast(x));
33}
34
35export fn entry9() void {
36 _ = @as(?(anyerror!u32), @bitCast(y));
37}
38export fn entry10() void {
39 const x: anyerror!u32 = undefined;
40 _ = @as(u32, @bitCast(x));
41}
42
43export fn entry11() void {
44 _ = @as(fn () void, @bitCast(y));
45}
46export fn entry12() void {
47 const x: fn () void = undefined;
48 _ = @as(u32, @bitCast(x));
49}
50
51export fn entry13() void {
52 _ = @as(noreturn, @bitCast(y));
53}
54
55export fn entry14() void {
56 _ = @as(@TypeOf(null), @bitCast(y));
57}
58export fn entry15() void {
59 const x: @TypeOf(null) = undefined;
60 _ = @as(u32, @bitCast(x));
61}
62
63export fn entry16() void {
64 const O = opaque {};
65 _ = @as(O, @bitCast(y));
66}
67
68export fn entry17() void {
69 _ = @as(??u32, @bitCast(y));
70}
71export fn entry18() void {
72 const x: ?u32 = undefined;
73 _ = @as(u32, @bitCast(x));
74}
75
76export fn entry19() void {
77 _ = @as(type, @bitCast(y));
78}
79export fn entry20() void {
80 const x: type = undefined;
81 _ = @as(u32, @bitCast(x));
82}
83
84export fn entry21() void {
85 _ = @as(@TypeOf(undefined), @bitCast(y));
86}
87export fn entry22() void {
88 const x: @TypeOf(undefined) = undefined;
89 _ = @as(u32, @bitCast(x));
90}
91
92export fn entry23() void {
93 _ = @as(void, @bitCast(y));
94}
95export fn entry24() void {
96 const x: void = undefined;
97 _ = @as(u32, @bitCast(x));
98}
99
100export fn entry25() void {
101 _ = @as(*u8, @bitCast(y));
102}
103export fn entry26() void {
104 const x: *u8 = undefined;
105 _ = @as(u32, @bitCast(x));
106}
107
108export fn entry27() void {
109 _ = @as(*u8, @bitCast(@as(*u32, @ptrFromInt(y))));
110}
111export fn entry28() void {
112 const x: *u8 = undefined;
113 _ = @as(*u32, @bitCast(x));
114}
115
116export fn entry29() void {
117 const S = struct { x: u32 };
118 _ = @as(S, @bitCast(y));
119}
120export fn entry30() void {
121 const S = struct { x: u32 };
122 const x: S = undefined;
123 _ = @as(u32, @bitCast(x));
124}
125
126export fn entry31() void {
127 const U = union { x: u32 };
128 _ = @as(U, @bitCast(y));
129}
130export fn entry32() void {
131 const U = union { x: u32 };
132 const x: U = undefined;
133 _ = @as(u32, @bitCast(x));
134}
135
136export fn entry33() void {
137 const S = struct { x: u32 };
138 _ = @as([10]S, @bitCast(y));
139}
140export fn entry34() void {
141 const S = struct { x: u32 };
142 const x: [10]S = undefined;
143 _ = @as(u32, @bitCast(x));
144}
145
146export fn entry35() void {
147 const E = enum {};
148 _ = @as(E, @bitCast(y));
149}
150
151export fn entry36() void {
152 const E = enum { a, b, c };
153 _ = @as(E, @bitCast(y));
154}
155
156// error
157//
158// :4:29: error: cannot @bitCast to 'comptime_float'
159// :8:27: error: cannot @bitCast from 'comptime_float'
160// :12:27: error: cannot @bitCast to 'comptime_int'
161// :16:27: error: cannot @bitCast from 'comptime_int'
162// :20:29: error: cannot @bitCast to '@EnumLiteral()'
163// :24:27: error: cannot @bitCast from '@EnumLiteral()'
164// :28:22: error: cannot @bitCast to 'error{}'
165// :32:27: error: cannot @bitCast from 'error{}'
166// :36:30: error: cannot @bitCast to 'anyerror!u32'
167// :40:27: error: cannot @bitCast from 'anyerror!u32'
168// :44:25: error: cannot @bitCast to 'fn () void'
169// :48:27: error: cannot @bitCast from 'fn () void'
170// :52:23: error: cannot @bitCast to 'noreturn'
171// :56:28: error: cannot @bitCast to '@TypeOf(null)'
172// :60:27: error: cannot @bitCast from '@TypeOf(null)'
173// :65:16: error: cannot @bitCast to 'tmp.entry16.O'
174// :64:15: note: opaque declared here
175// :69:20: error: cannot @bitCast to '?u32'
176// :69:20: note: use @ptrFromInt to cast from 'u32'
177// :73:27: error: cannot @bitCast from '?u32'
178// :73:27: note: use @intFromPtr to cast to 'u32'
179// :77:19: error: cannot @bitCast to 'type'
180// :81:27: error: cannot @bitCast from 'type'
181// :85:33: error: cannot @bitCast to '@TypeOf(undefined)'
182// :89:27: error: cannot @bitCast from '@TypeOf(undefined)'
183// :93:19: error: @bitCast size mismatch: destination type 'void' has 0 bits but source type 'u32' has 32 bits
184// :97:18: error: @bitCast size mismatch: destination type 'u32' has 32 bits but source type 'void' has 0 bits
185// :101:18: error: cannot @bitCast to '*u8'
186// :101:18: note: use @ptrFromInt to cast from 'u32'
187// :105:27: error: cannot @bitCast from '*u8'
188// :105:27: note: use @intFromPtr to cast to 'u32'
189// :109:49: error: pointer type '*u32' does not allow address zero
190// :113:19: error: cannot @bitCast to '*u32'
191// :113:19: note: use @ptrCast to cast from '*u8'
192// :118:16: error: cannot @bitCast to 'tmp.entry29.S'
193// :117:15: note: struct declared here
194// :123:27: error: cannot @bitCast from 'tmp.entry30.S'
195// :121:15: note: struct declared here
196// :128:16: error: cannot @bitCast to 'tmp.entry31.U'
197// :127:15: note: union declared here
198// :133:27: error: cannot @bitCast from 'tmp.entry32.U'
199// :131:15: note: union declared here
200// :138:20: error: cannot @bitCast to '[10]tmp.entry33.S'
201// :143:27: error: cannot @bitCast from '[10]tmp.entry34.S'
202// :148:16: error: cannot @bitCast to 'tmp.entry35.E'
203// :147:15: note: enum declared here
204// :153:16: error: cannot @bitCast to 'tmp.entry36.E'
205// :152:15: note: enum declared here
test/cases/compile_errors/bitcast_to_enum_invalid_tag_value.zig created+16
......@@ -0,0 +1,16 @@
1const E = enum(u8) { a, b, c };
2export fn entry1() void {
3 const x: E = @bitCast(@as(u8, 3));
4 _ = x;
5}
6
7export fn entry2() void {
8 const x: E = @bitCast(@as(u8, undefined));
9 _ = x;
10}
11
12// error
13//
14// :3:18: error: enum 'tmp.E' has no tag with value '3'
15// :1:11: note: enum declared here
16// :8:27: error: use of undefined value here causes illegal behavior
test/cases/compile_errors/empty_enum_from_backing_int.zig created+18
......@@ -0,0 +1,18 @@
1const E = enum(noreturn) {};
2
3export fn entry1() void {
4 const e: E = @fromBackingInt(undefined);
5 _ = e;
6}
7
8export fn entry2() void {
9 const e: E = @fromBackingInt(0);
10 _ = e;
11}
12
13// error
14//
15// :4:34: error: expected type 'noreturn', found '@TypeOf(undefined)'
16// :4:34: note: cannot coerce to uninstantiable type 'noreturn'
17// :9:34: error: expected type 'noreturn', found 'comptime_int'
18// :9:34: note: cannot coerce to uninstantiable type 'noreturn'
test/cases/compile_errors/enum_noreturn_backing_type.zig created+16
......@@ -0,0 +1,16 @@
1const E1 = enum(u32) {};
2export fn entry1() void {
3 const e: E1 = undefined;
4 _ = e;
5}
6
7const E2 = enum(noreturn) { a, b, c };
8export fn entry2() void {
9 const e: E2 = undefined;
10 _ = e;
11}
12
13// error
14//
15// :1:17: error: empty exhaustive enums must be backed by 'noreturn'
16// :7:17: error: non-empty enums cannot be backed by 'noreturn'
test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig+19-11
......@@ -1,15 +1,23 @@
1pub const E = enum(u31) { A, B, C };
2pub const S = extern struct {
3 e: E,
4};
5export fn entry() void {
6 const s: S = undefined;
7 _ = s;
1export fn entry1() void {
2 const E = enum(u31) { A, B, C };
3 _ = @sizeOf(extern struct {
4 x: E,
5 });
6}
7export fn entry2() void {
8 const E = enum(noreturn) {};
9 _ = @sizeOf(extern struct {
10 x: E,
11 });
812}
913
1014// error
1115//
12// :3:8: error: extern structs cannot contain fields of type 'tmp.E'
13// :1:15: note: enum tag type 'u31' is not extern compatible
14// :1:15: note: only integers with 0 or power of two bits are extern compatible
15// :1:15: note: enum declared here
16// :4:12: error: extern structs cannot contain fields of type 'tmp.entry1.E'
17// :2:15: note: enum tag type 'u31' is not extern compatible
18// :2:15: note: only integers with 0 or power of two bits are extern compatible
19// :2:15: note: enum declared here
20// :10:12: error: extern structs cannot contain fields of type 'tmp.entry2.E'
21// :8:15: note: enum tag type 'noreturn' is not extern compatible
22// :8:15: note: 'noreturn' is only allowed as a return type
23// :8:15: note: enum declared here
test/cases/compile_errors/from_backing_int_invalid_dest_type.zig created+37
......@@ -0,0 +1,37 @@
1const S1 = extern struct { x: u32 };
2export fn entry1(x: u32) void {
3 _ = @as(S1, @fromBackingInt(x));
4}
5
6const U1 = extern union { x: u32 };
7export fn entry2(x: u32) void {
8 _ = @as(U1, @fromBackingInt(x));
9}
10
11export fn entry3(x: u32) void {
12 _ = @as(u32, @fromBackingInt(x));
13}
14
15const S2 = packed struct { x: u32 };
16export fn entry4(x: u32) void {
17 _ = @as(S2, @fromBackingInt(x));
18}
19
20const U2 = packed union { x: u32 };
21export fn entry5(x: u32) u32 {
22 _ = @as(U2, @fromBackingInt(x));
23}
24
25// error
26//
27// :3:17: error: non-packed struct 'tmp.S1' does not have a backing integer
28// :1:19: note: struct declared here
29// :8:17: error: non-packed union 'tmp.U1' does not have a backing integer
30// :6:19: note: union declared here
31// :12:18: error: expected enum, packed union or packed struct, found 'u32'
32// :17:17: error: @fromBackingInt is ambiguous for type 'tmp.S2'
33// :15:19: note: backing integer type of struct is inferred
34// :15:19: note: consider explicitly specifying the backing integer type
35// :22:17: error: @fromBackingInt is ambiguous for type 'tmp.U2'
36// :20:19: note: backing integer type of union is inferred
37// :20:19: note: consider explicitly specifying the backing integer type
test/cases/compile_errors/from_backing_int_type_mismatch.zig created+23
......@@ -0,0 +1,23 @@
1const E = enum(u32) { a, b, c };
2export fn entry1(x: u64) E {
3 return @fromBackingInt(x);
4}
5
6const S = packed struct(u32) { x: u32 };
7export fn entry2(x: u64) S {
8 return @fromBackingInt(x);
9}
10
11const U = packed union(u32) { x: u32 };
12export fn entry3(x: u64) U {
13 return @fromBackingInt(x);
14}
15
16// error
17//
18// :3:28: error: expected type 'u32', found 'u64'
19// :3:28: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
20// :8:28: error: expected type 'u32', found 'u64'
21// :8:28: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
22// :13:28: error: expected type 'u32', found 'u64'
23// :13:28: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
test/cases/compile_errors/from_backing_int_undef.zig created+22
......@@ -0,0 +1,22 @@
1const E = enum(u32) { x };
2export fn entry1() void {
3 @compileLog(@as(E, @fromBackingInt(undefined)));
4}
5
6const S = packed struct(u32) { x: u32 };
7export fn entry2() void {
8 @compileLog(@as(S, @fromBackingInt(undefined)));
9}
10
11const U = packed union(u32) { x: u32 };
12export fn entry3() void {
13 @compileLog(@as(U, @fromBackingInt(undefined)));
14}
15
16// error
17//
18// :3:40: error: use of undefined value here causes illegal behavior
19//
20// Compile Log Output:
21// @as(tmp.S, undefined)
22// @as(tmp.U, undefined)
test/cases/compile_errors/initialize_empty_union.zig+2-2
......@@ -1,10 +1,10 @@
11const EnumInferred = enum {};
2const EnumExplicit = enum(u8) {};
2const EnumExplicit = enum(noreturn) {};
33const EnumNonexhaustive = enum(u8) { _ };
44
55const U0 = union {};
66const U1 = union(enum) {};
7const U2 = union(enum(u8)) {};
7const U2 = union(enum(noreturn)) {};
88const U3 = union(EnumInferred) {};
99const U4 = union(EnumExplicit) {};
1010const U5 = union(EnumNonexhaustive) {};
test/cases/compile_errors/instantiate_empty_enum.zig created+32
......@@ -0,0 +1,32 @@
1const E = enum {};
2
3export fn entry1() void {
4 const e: E = undefined;
5 _ = e;
6}
7
8export fn entry2() void {
9 const e: E = @enumFromInt(@as(u8, undefined));
10 _ = e;
11}
12
13export fn entry3() void {
14 const e: E = .a;
15 _ = e;
16}
17
18export fn entry4() void {
19 const e: E = @enumFromInt(0);
20 _ = e;
21}
22
23// error
24//
25// :4:18: error: expected type 'tmp.E', found '@TypeOf(undefined)'
26// :4:18: note: cannot coerce to uninstantiable type 'tmp.E'
27// :1:11: note: enum declared here
28// :9:31: error: use of undefined value here causes illegal behavior
29// :14:19: error: enum 'tmp.E' has no member named 'a'
30// :1:11: note: enum declared here
31// :19:18: error: enum 'tmp.E' has no tag with value '0'
32// :1:11: note: enum declared here
test/cases/compile_errors/int_from_enum_undefined.zig deleted-11
......@@ -1,11 +0,0 @@
1export fn a() void {
2 const E = enum {};
3 var e: E = undefined;
4 _ = &e;
5 _ = @intFromEnum(e);
6}
7
8// error
9//
10// :5:22: error: cannot use @intFromEnum on empty enum 'tmp.a.E'
11// :2:15: note: enum declared here
test/cases/compile_errors/invalid_non-exhaustive_enum_to_union.zig+1-1
......@@ -22,5 +22,5 @@ export fn bar() void {
2222//
2323// :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum
2424// :1:11: note: enum declared here
25// :17:16: error: union 'tmp.U' has no tag with value '@enumFromInt(15)'
25// :17:16: error: union 'tmp.U' has no tag with value '@fromBackingInt(15)'
2626// :6:11: note: union declared here
test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig+8
......@@ -81,6 +81,12 @@ export fn entry15() void {
8181 x: *const u32,
8282 });
8383}
84export fn entry16() void {
85 const E = enum(noreturn) {};
86 _ = @sizeOf(packed struct {
87 x: E,
88 });
89}
8490
8591// error
8692//
......@@ -114,3 +120,5 @@ export fn entry15() void {
114120// :81:12: error: packed structs cannot contain fields of type '*const u32'
115121// :81:12: note: pointers cannot be directly bitpacked
116122// :81:12: note: consider using 'usize' and '@intFromPtr'
123// :87:12: error: packed structs cannot contain fields of type 'tmp.entry16.E'
124// :87:12: note: type does not have a bit-packed representation
test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig+8
......@@ -10,6 +10,12 @@ export fn entry1() void {
1010 x: *const u32,
1111 });
1212}
13export fn entry2() void {
14 const E = enum(noreturn) {};
15 _ = @sizeOf(packed union {
16 x: E,
17 });
18}
1319
1420// error
1521//
......@@ -19,3 +25,5 @@ export fn entry1() void {
1925// :10:12: error: packed unions cannot contain fields of type '*const u32'
2026// :10:12: note: pointers cannot be directly bitpacked
2127// :10:12: note: consider using 'usize' and '@intFromPtr'
28// :16:12: error: packed unions cannot contain fields of type 'tmp.entry2.E'
29// :16:12: note: type does not have a bit-packed representation
test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig+1-1
......@@ -1,4 +1,4 @@
1const Tag = @Enum(u0, .exhaustive, &.{}, &.{});
1const Tag = @Enum(noreturn, .exhaustive, &.{}, &.{});
22const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{}));
33export fn entry() void {
44 const tagged: Tagged = undefined;
test/cases/compile_errors/sizeof_alignof_empty_union.zig+2-2
......@@ -1,10 +1,10 @@
11const EnumInferred = enum {};
2const EnumExplicit = enum(u8) {};
2const EnumExplicit = enum(noreturn) {};
33const EnumNonexhaustive = enum(u8) { _ };
44
55const U0 = union {};
66const U1 = union(enum) {};
7const U2 = union(enum(u8)) {};
7const U2 = union(enum(noreturn)) {};
88const U3 = union(EnumInferred) {};
99const U4 = union(EnumExplicit) {};
1010const U5 = union(EnumNonexhaustive) {};
test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig+1-1
......@@ -6,5 +6,5 @@ test "enum" {
66// error
77// is_test=true
88//
9// :3:9: error: no field with value '@enumFromInt(5)' in enum 'tmp.test.enum.E'
9// :3:9: error: no field with value '@fromBackingInt(5)' in enum 'tmp.test.enum.E'
1010// :2:15: note: declared here
test/cases/safety/backing_int_no_matching_tag_value.zig created+25
......@@ -0,0 +1,25 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid enum value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const Foo = enum(u8) {
11 a,
12 b,
13 c,
14};
15pub fn main() !void {
16 _ = bar(3);
17 return error.TestFailed;
18}
19fn bar(a: u8) Foo {
20 return @fromBackingInt(a);
21}
22
23// run
24// backend=selfhosted,llvm
25// target=x86_64-linux,aarch64-linux
test/cases/safety/bitcast_to_enum_no_matching_tag_value.zig created+25
......@@ -0,0 +1,25 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid enum value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const Foo = enum(u8) {
11 a,
12 b,
13 c,
14};
15pub fn main() !void {
16 _ = bar(3);
17 return error.TestFailed;
18}
19fn bar(a: u8) Foo {
20 return @bitCast(a);
21}
22
23// run
24// backend=selfhosted,llvm
25// target=x86_64-linux,aarch64-linux