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...@@ -2271,6 +2271,7 @@ or
2271 This even works at {#link|comptime#}:2271 This even works at {#link|comptime#}:
2272 </p>2272 </p>
2273 {#code|test_packed_structs.zig#}2273 {#code|test_packed_structs.zig#}
2274
2274 <p>2275 <p>
2275 The backing integer can be inferred or explicitly provided. When2276 The backing integer can be inferred or explicitly provided. When
2276 inferred, it will be unsigned. When explicitly provided, its bit width2277 inferred, it will be unsigned. When explicitly provided, its bit width
...@@ -2279,6 +2280,12 @@ or...@@ -2279,6 +2280,12 @@ or
2279 </p>2280 </p>
2280 {#code|test_missized_packed_struct.zig#}2281 {#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
2282 <p>2289 <p>
2283 Zig allows the address to be taken of a non-byte-aligned field:2290 Zig allows the address to be taken of a non-byte-aligned field:
2284 </p>2291 </p>
...@@ -2401,7 +2408,7 @@ or...@@ -2401,7 +2408,7 @@ or
2401 {#header_open|enum#}2408 {#header_open|enum#}
2402 {#code|test_enums.zig#}2409 {#code|test_enums.zig#}
24032410
2404 {#see_also|@typeInfo|@tagName|@sizeOf#}2411 {#see_also|@backingInt|@fromBackingInt|@typeInfo|@tagName|@sizeOf|noreturn#}
24052412
2406 {#header_open|extern enum#}2413 {#header_open|extern enum#}
2407 <p>2414 <p>
...@@ -2431,9 +2438,7 @@ or...@@ -2431,9 +2438,7 @@ or
2431 The enum must specify a tag type and cannot consume every enumeration value.2438 The enum must specify a tag type and cannot consume every enumeration value.
2432 </p>2439 </p>
2433 <p>2440 <p>
2434 {#link|@enumFromInt#} on a non-exhaustive enum involves the safety semantics2441 {#link|@fromBackingInt#} on a non-exhaustive enum always results in a valid enum value.
2435 of {#link|@intCast#} to the integer tag type, but beyond that always results in
2436 a well-defined enum value.
2437 </p>2442 </p>
2438 <p>2443 <p>
2439 A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.2444 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...@@ -2461,7 +2466,8 @@ or
2461 {#code|test_simple_union.zig#}2466 {#code|test_simple_union.zig#}
24622467
2463 <p>2468 <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#}.
2465 </p>2471 </p>
2466 <p>2472 <p>
2467 To initialize a union when the tag is a {#link|comptime#}-known name, see {#link|@unionInit#}.2473 To initialize a union when the tag is a {#link|comptime#}-known name, see {#link|@unionInit#}.
...@@ -2490,7 +2496,7 @@ or...@@ -2490,7 +2496,7 @@ or
2490 <p>2496 <p>
2491 Unions with inferred enum tag types can also assign ordinal values to their inferred tag.2497 Unions with inferred enum tag types can also assign ordinal values to their inferred tag.
2492 This requires the tag to specify an explicit integer type.2498 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.
2494 </p>2500 </p>
2495 {#code|test_tagged_union_with_tag_values.zig#}2501 {#code|test_tagged_union_with_tag_values.zig#}
24962502
...@@ -2521,6 +2527,7 @@ or...@@ -2521,6 +2527,7 @@ or
2521 </p>2527 </p>
2522 {#code|test_packed_union_equality.zig#}2528 {#code|test_packed_union_equality.zig#}
25232529
2530 {#see_also|@backingInt|@fromBackingInt#}
2524 {#header_close#}2531 {#header_close#}
25252532
2526 {#header_open|Anonymous Union Literals#}2533 {#header_open|Anonymous Union Literals#}
...@@ -3586,14 +3593,14 @@ void do_a_thing(struct Foo *foo) {...@@ -3586,14 +3593,14 @@ void do_a_thing(struct Foo *foo) {
3586 <ul>3593 <ul>
3587 <li>{#link|@bitCast#} - change type but maintain bit representation</li>3594 <li>{#link|@bitCast#} - change type but maintain bit representation</li>
3588 <li>{#link|@alignCast#} - make a pointer have more alignment</li>3595 <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>
3590 <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>3597 <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>
3591 <li>{#link|@errorCast#} - convert to a smaller error set</li>3598 <li>{#link|@errorCast#} - convert to a smaller error set</li>
3592 <li>{#link|@floatCast#} - convert a larger float to a smaller float</li>3599 <li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
3593 <li>{#link|@floatFromInt#} - convert an integer to a float value</li>3600 <li>{#link|@floatFromInt#} - convert an integer to a float value</li>
3594 <li>{#link|@intCast#} - convert between integer types</li>3601 <li>{#link|@intCast#} - convert between integer types</li>
3595 <li>{#link|@intFromBool#} - convert true to 1 and false to 0</li>3602 <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>
3597 <li>{#link|@intFromError#} - obtain the integer value of an error code</li>3604 <li>{#link|@intFromError#} - obtain the integer value of an error code</li>
3598 <li>{#link|@round#}, {#link|@floor#}, {#link|@ceil#}, {#link|@trunc#} - float to integer conversion</li>3605 <li>{#link|@round#}, {#link|@floor#}, {#link|@ceil#}, {#link|@trunc#} - float to integer conversion</li>
3599 <li>{#link|@intFromPtr#} - obtain the address of a pointer</li>3606 <li>{#link|@intFromPtr#} - obtain the address of a pointer</li>
...@@ -4432,6 +4439,18 @@ comptime {...@@ -4432,6 +4439,18 @@ comptime {
4432 {#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}4439 {#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
4433 {#header_close#}4440 {#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
4435 {#header_open|@bitCast#}4454 {#header_open|@bitCast#}
4436 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>4455 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
4437 <p>4456 <p>
...@@ -4452,8 +4471,18 @@ comptime {...@@ -4452,8 +4471,18 @@ comptime {
4452 <li>Convert {#syntax#}i32{#endsyntax#} to {#syntax#}u32{#endsyntax#} preserving twos complement</li>4471 <li>Convert {#syntax#}i32{#endsyntax#} to {#syntax#}u32{#endsyntax#} preserving twos complement</li>
4453 </ul>4472 </ul>
4454 <p>4473 <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#}.
4456 </p>4484 </p>
4485 {#see_also|@ptrCast|@intFromPtr|@ptrFromInt|@errorCast|@intFromError|@errorFromInt|@backingInt|@fromBackingInt#}
4457 {#header_close#}4486 {#header_close#}
44584487
4459 {#header_open|@bitOffsetOf#}4488 {#header_open|@bitOffsetOf#}
...@@ -4798,6 +4827,9 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4798,6 +4827,9 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4798 {#header_open|@enumFromInt#}4827 {#header_open|@enumFromInt#}
4799 <pre>{#syntax#}@enumFromInt(integer: anytype) anytype{#endsyntax#}</pre>4828 <pre>{#syntax#}@enumFromInt(integer: anytype) anytype{#endsyntax#}</pre>
4800 <p>4829 <p>
4830 Deprecated. Use {#link|@fromBackingInt#} or {#link|@bitCast#} instead.
4831 </p>
4832 <p>
4801 Converts an integer into an {#link|enum#} value. The return type is the inferred result type.4833 Converts an integer into an {#link|enum#} value. The return type is the inferred result type.
4802 </p>4834 </p>
4803 <p>4835 <p>
...@@ -4953,6 +4985,22 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4953,6 +4985,22 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4953 </p>4985 </p>
4954 {#header_close#}4986 {#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
4956 {#header_open|@hasDecl#}5004 {#header_open|@hasDecl#}
4957 <pre>{#syntax#}@hasDecl(comptime Namespace: type, comptime name: []const u8) bool{#endsyntax#}</pre>5005 <pre>{#syntax#}@hasDecl(comptime Namespace: type, comptime name: []const u8) bool{#endsyntax#}</pre>
4958 <p>Returns whether or not a {#link|Namespace#} has a declaration matching {#syntax#}name{#endsyntax#}.</p>5006 <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...@@ -5035,12 +5083,11 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5035 {#header_open|@intFromEnum#}5083 {#header_open|@intFromEnum#}
5036 <pre>{#syntax#}@intFromEnum(enum_or_tagged_union: anytype) anytype{#endsyntax#}</pre>5084 <pre>{#syntax#}@intFromEnum(enum_or_tagged_union: anytype) anytype{#endsyntax#}</pre>
5037 <p>5085 <p>
5038 Converts an enumeration value into its integer tag type. When a tagged union is passed,5086 Deprecated. Use {#link|@backingInt#} or {#link|@bitCast#} instead.
5039 the tag value is used as the enumeration value.
5040 </p>5087 </p>
5041 <p>5088 <p>
5042 If there is only one possible enum value, the result is a {#syntax#}comptime_int{#endsyntax#}5089 Converts an enumeration value into its integer tag type. When a tagged union is passed,
5043 known at {#link|comptime#}.5090 the tag value is used as the enumeration value.
5044 </p>5091 </p>
5045 {#see_also|@enumFromInt#}5092 {#see_also|@enumFromInt#}
5046 {#header_close#}5093 {#header_close#}
...@@ -5077,7 +5124,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -5077,7 +5124,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5077 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer.5124 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer.
5078 {#syntax#}value{#endsyntax#} can be {#syntax#}*T{#endsyntax#} or {#syntax#}?*T{#endsyntax#}.5125 {#syntax#}value{#endsyntax#} can be {#syntax#}*T{#endsyntax#} or {#syntax#}?*T{#endsyntax#}.
5079 </p>5126 </p>
5080 <p>To convert the other way, use {#link|@ptrFromInt#}</p>5127 {#see_also|@ptrFromInt#}
5081 {#header_close#}5128 {#header_close#}
50825129
5083 {#header_open|@max#}5130 {#header_open|@max#}
...@@ -5281,6 +5328,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -5281,6 +5328,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5281 If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}5328 If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}
5282 is zero, this invokes safety-checked {#link|Illegal Behavior#}.5329 is zero, this invokes safety-checked {#link|Illegal Behavior#}.
5283 </p>5330 </p>
5331 {#see_also|@intFromPtr#}
5284 {#header_close#}5332 {#header_close#}
52855333
5286 {#header_open|@rem#}5334 {#header_open|@rem#}
doc/langref/runtime_invalid_enum_cast.zig+7-5
...@@ -1,16 +1,18 @@...@@ -1,16 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3const Foo = enum {3const Foo = enum(u2) {
4 a,4 a,
5 b,5 b,
6 c,6 c,
7};7};
88
9pub fn main() void {9fn foo(a: u2) void {
10 var a: u2 = 3;10 const b: Foo = @fromBackingInt(a);
11 _ = &a;
12 const b: Foo = @enumFromInt(a);
13 std.debug.print("value: {s}\n", .{@tagName(b)});11 std.debug.print("value: {s}\n", .{@tagName(b)});
14}12}
1513
14pub fn main() void {
15 foo(3);
16}
17
16// exe=fail18// exe=fail
doc/langref/test_comptime_invalid_enum_cast.zig+2-2
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1const Foo = enum {1const Foo = enum(u2) {
2 a,2 a,
3 b,3 b,
4 c,4 c,
5};5};
6comptime {6comptime {
7 const a: u2 = 3;7 const a: u2 = 3;
8 const b: Foo = @enumFromInt(a);8 const b: Foo = @fromBackingInt(a);
9 _ = b;9 _ = b;
10}10}
1111
doc/langref/test_enums.zig+17-11
...@@ -22,9 +22,9 @@ const Value = enum(u2) {...@@ -22,9 +22,9 @@ const Value = enum(u2) {
22// Now you can cast between u2 and Value.22// Now you can cast between u2 and Value.
23// The ordinal value starts from 0, counting up by 1 from the previous member.23// The ordinal value starts from 0, counting up by 1 from the previous member.
24test "enum ordinal value" {24test "enum ordinal value" {
25 try expectEqual(0, @intFromEnum(Value.zero));25 try expectEqual(0, @backingInt(Value.zero));
26 try expectEqual(1, @intFromEnum(Value.one));26 try expectEqual(1, @backingInt(Value.one));
27 try expectEqual(2, @intFromEnum(Value.two));27 try expectEqual(2, @backingInt(Value.two));
28}28}
2929
30// You can override the ordinal value for an enum.30// You can override the ordinal value for an enum.
...@@ -34,9 +34,9 @@ const Value2 = enum(u32) {...@@ -34,9 +34,9 @@ const Value2 = enum(u32) {
34 million = 1000000,34 million = 1000000,
35};35};
36test "set enum ordinal value" {36test "set enum ordinal value" {
37 try expectEqual(100, @intFromEnum(Value2.hundred));37 try expectEqual(100, @backingInt(Value2.hundred));
38 try expectEqual(1000, @intFromEnum(Value2.thousand));38 try expectEqual(1000, @backingInt(Value2.thousand));
39 try expectEqual(1000000, @intFromEnum(Value2.million));39 try expectEqual(1000000, @backingInt(Value2.million));
40}40}
4141
42// You can also override only some values.42// You can also override only some values.
...@@ -48,11 +48,11 @@ const Value3 = enum(u4) {...@@ -48,11 +48,11 @@ const Value3 = enum(u4) {
48 e,48 e,
49};49};
50test "enum implicit ordinal values and overridden values" {50test "enum implicit ordinal values and overridden values" {
51 try expectEqual(0, @intFromEnum(Value3.a));51 try expectEqual(0, @backingInt(Value3.a));
52 try expectEqual(8, @intFromEnum(Value3.b));52 try expectEqual(8, @backingInt(Value3.b));
53 try expectEqual(9, @intFromEnum(Value3.c));53 try expectEqual(9, @backingInt(Value3.c));
54 try expectEqual(4, @intFromEnum(Value3.d));54 try expectEqual(4, @backingInt(Value3.d));
55 try expectEqual(5, @intFromEnum(Value3.e));55 try expectEqual(5, @backingInt(Value3.e));
56}56}
5757
58// Enums can have methods, the same as structs and unions.58// Enums can have methods, the same as structs and unions.
...@@ -111,4 +111,10 @@ test "@tagName" {...@@ -111,4 +111,10 @@ test "@tagName" {
111 try expectEqualStrings(@tagName(Small.three), "three");111 try expectEqualStrings(@tagName(Small.three), "three");
112}112}
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
114// test120// test
doc/langref/test_inline_else.zig+2-2
...@@ -9,7 +9,7 @@ const SliceTypeB = extern struct {...@@ -9,7 +9,7 @@ const SliceTypeB = extern struct {
9 ptr: [*]SliceTypeA,9 ptr: [*]SliceTypeA,
10 len: usize,10 len: usize,
11};11};
12const AnySlice = union(enum) {12const AnySlice = union(enum(u8)) {
13 a: SliceTypeA,13 a: SliceTypeA,
14 b: SliceTypeB,14 b: SliceTypeB,
15 c: []const u8,15 c: []const u8,
...@@ -23,7 +23,7 @@ fn withFor(any: AnySlice) usize {...@@ -23,7 +23,7 @@ fn withFor(any: AnySlice) usize {
23 // With `inline for` the function gets generated as23 // With `inline for` the function gets generated as
24 // a series of `if` statements relying on the optimizer24 // a series of `if` statements relying on the optimizer
25 // to convert it to a switch.25 // to convert it to a switch.
26 if (field_value == @intFromEnum(any)) {26 if (field_value == @backingInt(any)) {
27 return @field(any, field_name).len;27 return @field(any, field_name).len;
28 }28 }
29 }29 }
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)) {...@@ -8,10 +8,10 @@ const Tagged = union(enum(u32)) {
88
9test "tag values" {9test "tag values" {
10 const int: Tagged = .{ .int = -40 };10 const int: Tagged = .{ .int = -40 };
11 try expectEqual(123, @intFromEnum(int));11 try expectEqual(123, @backingInt(int));
1212
13 const boolean: Tagged = .{ .boolean = false };13 const boolean: Tagged = .{ .boolean = false };
14 try expectEqual(67, @intFromEnum(boolean));14 try expectEqual(67, @backingInt(boolean));
15}15}
1616
17// test17// test
lib/std/meta.zig+35-3
...@@ -408,7 +408,8 @@ pub fn FieldEnum(comptime T: type) type {...@@ -408,7 +408,8 @@ pub fn FieldEnum(comptime T: type) type {
408 else => {},408 else => {},
409 }409 }
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);
412 return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len));413 return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len));
413}414}
414415
...@@ -469,7 +470,8 @@ test FieldEnum {...@@ -469,7 +470,8 @@ test FieldEnum {
469470
470pub fn DeclEnum(comptime T: type) type {471pub fn DeclEnum(comptime T: type) type {
471 const decl_names = declarations(T);472 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);
473 return @Enum(IntTag, .exhaustive, decl_names, &std.simd.iota(IntTag, decl_names.len));475 return @Enum(IntTag, .exhaustive, decl_names, &std.simd.iota(IntTag, decl_names.len));
474}476}
475477
...@@ -504,10 +506,40 @@ pub fn BareUnion(comptime T: type) type {...@@ -504,10 +506,40 @@ pub fn BareUnion(comptime T: type) type {
504 .@"union" => |u| u,506 .@"union" => |u| u,
505 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),507 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),
506 };508 };
507
508 return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);509 return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);
509}510}
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
511pub fn Tag(comptime T: type) type {543pub fn Tag(comptime T: type) type {
512 return switch (@typeInfo(T)) {544 return switch (@typeInfo(T)) {
513 .@"enum" => |info| info.tag_type,545 .@"enum" => |info| info.tag_type,
lib/std/zig.zig-2
...@@ -906,7 +906,6 @@ pub const SimpleComptimeReason = enum(u32) {...@@ -906,7 +906,6 @@ pub const SimpleComptimeReason = enum(u32) {
906 slice_single_item_ptr_bounds,906 slice_single_item_ptr_bounds,
907 stored_to_comptime_field,907 stored_to_comptime_field,
908 stored_to_comptime_var,908 stored_to_comptime_var,
909 casted_to_comptime_enum,
910 casted_to_comptime_int,909 casted_to_comptime_int,
911 casted_to_comptime_float,910 casted_to_comptime_float,
912 std_lang_decl,911 std_lang_decl,
...@@ -989,7 +988,6 @@ pub const SimpleComptimeReason = enum(u32) {...@@ -989,7 +988,6 @@ pub const SimpleComptimeReason = enum(u32) {
989 .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds",988 .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds",
990 .stored_to_comptime_field => "value stored to a comptime field must be comptime-known",989 .stored_to_comptime_field => "value stored to a comptime field must be comptime-known",
991 .stored_to_comptime_var => "value stored to a comptime variable must be comptime-known",990 .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",
993 .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known",991 .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known",
994 .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known",992 .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known",
995 .std_lang_decl => "'std.lang' declaration values must be comptime-known",993 .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(...@@ -1652,7 +1652,17 @@ fn renderBuiltinCall(
1652 const tree = r.tree;1652 const tree = r.tree;
1653 const ais = r.ais;1653 const ais = r.ais;
16541654
1655 try renderToken(r, builtin_token, .none); // @name1655 // 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
1657 if (r.fixups.rebase_imported_paths) |prefix| {1667 if (r.fixups.rebase_imported_paths) |prefix| {
1658 const slice = tree.tokenSlice(builtin_token);1668 const slice = tree.tokenSlice(builtin_token);
...@@ -1675,7 +1685,14 @@ fn renderBuiltinCall(...@@ -1675,7 +1685,14 @@ fn renderBuiltinCall(
1675 }1685 }
1676 }1686 }
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);
1679}1696}
16801697
1681fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex {1698fn 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...@@ -2703,6 +2703,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2703 .elem_type,2703 .elem_type,
2704 .indexable_ptr_elem_type,2704 .indexable_ptr_elem_type,
2705 .splat_op_result_ty,2705 .splat_op_result_ty,
2706 .from_backing_int_arg_ty,
2706 .reify_int,2707 .reify_int,
2707 .vector_type,2708 .vector_type,
2708 .indexable_ptr_len,2709 .indexable_ptr_len,
...@@ -2803,6 +2804,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2803,6 +2804,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2803 .error_set_decl,2804 .error_set_decl,
2804 .enum_from_int,2805 .enum_from_int,
2805 .int_from_enum,2806 .int_from_enum,
2807 .backing_int,
2808 .from_backing_int,
2806 .type_info,2809 .type_info,
2807 .size_of,2810 .size_of,
2808 .bit_size_of,2811 .bit_size_of,
...@@ -9168,6 +9171,7 @@ fn builtinCall(...@@ -9168,6 +9171,7 @@ fn builtinCall(
9168 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),9171 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
9169 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),9172 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
9170 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),9173 .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),
9171 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),9175 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
9172 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),9176 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
9173 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),9177 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
...@@ -9199,6 +9203,20 @@ fn builtinCall(...@@ -9199,6 +9203,20 @@ fn builtinCall(
9199 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),9203 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
9200 // zig fmt: on9204 // 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
9202 .in_comptime => if (gz.is_comptime) {9220 .in_comptime => if (gz.is_comptime) {
9203 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});9221 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
9204 } else {9222 } else {
lib/std/zig/AstRlAnnotate.zig+2
...@@ -889,6 +889,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -889,6 +889,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
889 .int_from_bool,889 .int_from_bool,
890 .int_from_error,890 .int_from_error,
891 .error_from_int,891 .error_from_int,
892 .from_backing_int,
892 .embed_file,893 .embed_file,
893 .error_name,894 .error_name,
894 .set_runtime_safety,895 .set_runtime_safety,
...@@ -916,6 +917,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -916,6 +917,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
916 .float_from_int,917 .float_from_int,
917 .ptr_from_int,918 .ptr_from_int,
918 .enum_from_int,919 .enum_from_int,
920 .backing_int,
919 .float_cast,921 .float_cast,
920 .int_cast,922 .int_cast,
921 .truncate,923 .truncate,
lib/std/zig/BuiltinFn.zig+16
...@@ -56,6 +56,8 @@ pub const Tag = enum {...@@ -56,6 +56,8 @@ pub const Tag = enum {
56 import,56 import,
57 in_comptime,57 in_comptime,
58 int_cast,58 int_cast,
59 backing_int,
60 from_backing_int,
59 enum_from_int,61 enum_from_int,
60 error_from_int,62 error_from_int,
61 float_from_int,63 float_from_int,
...@@ -564,6 +566,20 @@ pub const list = list: {...@@ -564,6 +566,20 @@ pub const list = list: {
564 .param_count = 1,566 .param_count = 1,
565 },567 },
566 },568 },
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 },
567 .{583 .{
568 "@enumFromInt",584 "@enumFromInt",
569 .{585 .{
lib/std/zig/Zir.zig+28-2
...@@ -283,6 +283,14 @@ pub const Inst = struct {...@@ -283,6 +283,14 @@ pub const Inst = struct {
283 ///283 ///
284 /// Uses the `un_node` field.284 /// Uses the `un_node` field.
285 splat_op_result_ty,285 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,
286 /// Given a pointer to an indexable object, returns the len property. This is294 /// Given a pointer to an indexable object, returns the len property. This is
287 /// used by for loops. This instruction also emits a for-loop specific compile295 /// used by for loops. This instruction also emits a for-loop specific compile
288 /// error if the indexable object is not indexable.296 /// error if the indexable object is not indexable.
...@@ -872,6 +880,9 @@ pub const Inst = struct {...@@ -872,6 +880,9 @@ pub const Inst = struct {
872 /// Converts an enum value into an integer. Resulting type will be the tag type880 /// Converts an enum value into an integer. Resulting type will be the tag type
873 /// of the enum. Uses `un_node`.881 /// of the enum. Uses `un_node`.
874 int_from_enum,882 int_from_enum,
883 /// Implements the `@backingInt` builtin.
884 /// Uses `un_node`.
885 backing_int,
875 /// Implement builtin `@alignOf`. Uses `un_node`.886 /// Implement builtin `@alignOf`. Uses `un_node`.
876 align_of,887 align_of,
877 /// Implement builtin `@intFromBool`. Uses `un_node`.888 /// Implement builtin `@intFromBool`. Uses `un_node`.
...@@ -934,6 +945,9 @@ pub const Inst = struct {...@@ -934,6 +945,9 @@ pub const Inst = struct {
934 /// Converts an integer into an enum value.945 /// Converts an integer into an enum value.
935 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.946 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
936 enum_from_int,947 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,
937 /// Convert a larger float type to any other float type, possibly causing951 /// Convert a larger float type to any other float type, possibly causing
938 /// a loss of precision.952 /// a loss of precision.
939 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.953 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
...@@ -1115,6 +1129,7 @@ pub const Inst = struct {...@@ -1115,6 +1129,7 @@ pub const Inst = struct {
1115 .elem_type,1129 .elem_type,
1116 .indexable_ptr_elem_type,1130 .indexable_ptr_elem_type,
1117 .splat_op_result_ty,1131 .splat_op_result_ty,
1132 .from_backing_int_arg_ty,
1118 .indexable_ptr_len,1133 .indexable_ptr_len,
1119 .anyframe_type,1134 .anyframe_type,
1120 .as_node,1135 .as_node,
...@@ -1229,6 +1244,8 @@ pub const Inst = struct {...@@ -1229,6 +1244,8 @@ pub const Inst = struct {
1229 .field_type_ref,1244 .field_type_ref,
1230 .enum_from_int,1245 .enum_from_int,
1231 .int_from_enum,1246 .int_from_enum,
1247 .backing_int,
1248 .from_backing_int,
1232 .type_info,1249 .type_info,
1233 .size_of,1250 .size_of,
1234 .bit_size_of,1251 .bit_size_of,
...@@ -1409,6 +1426,7 @@ pub const Inst = struct {...@@ -1409,6 +1426,7 @@ pub const Inst = struct {
1409 .elem_type,1426 .elem_type,
1410 .indexable_ptr_elem_type,1427 .indexable_ptr_elem_type,
1411 .splat_op_result_ty,1428 .splat_op_result_ty,
1429 .from_backing_int_arg_ty,
1412 .indexable_ptr_len,1430 .indexable_ptr_len,
1413 .anyframe_type,1431 .anyframe_type,
1414 .as_node,1432 .as_node,
...@@ -1511,6 +1529,8 @@ pub const Inst = struct {...@@ -1511,6 +1529,8 @@ pub const Inst = struct {
1511 .field_type_ref,1529 .field_type_ref,
1512 .enum_from_int,1530 .enum_from_int,
1513 .int_from_enum,1531 .int_from_enum,
1532 .backing_int,
1533 .from_backing_int,
1514 .type_info,1534 .type_info,
1515 .size_of,1535 .size_of,
1516 .bit_size_of,1536 .bit_size_of,
...@@ -1645,6 +1665,7 @@ pub const Inst = struct {...@@ -1645,6 +1665,7 @@ pub const Inst = struct {
1645 .elem_type = .un_node,1665 .elem_type = .un_node,
1646 .indexable_ptr_elem_type = .un_node,1666 .indexable_ptr_elem_type = .un_node,
1647 .splat_op_result_ty = .un_node,1667 .splat_op_result_ty = .un_node,
1668 .from_backing_int_arg_ty = .un_node,
1648 .indexable_ptr_len = .un_node,1669 .indexable_ptr_len = .un_node,
1649 .anyframe_type = .un_node,1670 .anyframe_type = .un_node,
1650 .as_node = .pl_node,1671 .as_node = .pl_node,
...@@ -1774,6 +1795,7 @@ pub const Inst = struct {...@@ -1774,6 +1795,7 @@ pub const Inst = struct {
1774 .compile_error = .un_node,1795 .compile_error = .un_node,
1775 .set_eval_branch_quota = .un_node,1796 .set_eval_branch_quota = .un_node,
1776 .int_from_enum = .un_node,1797 .int_from_enum = .un_node,
1798 .backing_int = .un_node,
1777 .align_of = .un_node,1799 .align_of = .un_node,
1778 .int_from_bool = .un_node,1800 .int_from_bool = .un_node,
1779 .embed_file = .un_node,1801 .embed_file = .un_node,
...@@ -1803,6 +1825,7 @@ pub const Inst = struct {...@@ -1803,6 +1825,7 @@ pub const Inst = struct {
1803 .float_from_int = .pl_node,1825 .float_from_int = .pl_node,
1804 .ptr_from_int = .pl_node,1826 .ptr_from_int = .pl_node,
1805 .enum_from_int = .pl_node,1827 .enum_from_int = .pl_node,
1828 .from_backing_int = .pl_node,
1806 .float_cast = .pl_node,1829 .float_cast = .pl_node,
1807 .int_cast = .pl_node,1830 .int_cast = .pl_node,
1808 .ptr_cast = .pl_node,1831 .ptr_cast = .pl_node,
...@@ -2160,8 +2183,8 @@ pub const Inst = struct {...@@ -2160,8 +2183,8 @@ pub const Inst = struct {
2160 astgen_error,2183 astgen_error,
2161 /// Given a type, strips away any error unions or optionals stacked2184 /// Given a type, strips away any error unions or optionals stacked
2162 /// on top and returns the base type. That base type must be a float.2185 /// on top and returns the base type. That base type must be a float.
2163 /// For example: Provided with error{Foo}!?f64, returns f64.2186 /// For example: Provided with `error{Foo}!?f64`, returns `f64`.
2164 /// `operand` is `operand: Air.Inst.Ref`.2187 /// `operand` is payload index to `UnNode`.
2165 float_op_result_ty,2188 float_op_result_ty,
21662189
2167 pub const InstData = struct {2190 pub const InstData = struct {
...@@ -4141,6 +4164,7 @@ fn findTrackableInner(...@@ -4141,6 +4164,7 @@ fn findTrackableInner(
4141 .elem_type,4164 .elem_type,
4142 .indexable_ptr_elem_type,4165 .indexable_ptr_elem_type,
4143 .splat_op_result_ty,4166 .splat_op_result_ty,
4167 .from_backing_int_arg_ty,
4144 .indexable_ptr_len,4168 .indexable_ptr_len,
4145 .anyframe_type,4169 .anyframe_type,
4146 .as_node,4170 .as_node,
...@@ -4296,6 +4320,8 @@ fn findTrackableInner(...@@ -4296,6 +4320,8 @@ fn findTrackableInner(
4296 .float_from_int,4320 .float_from_int,
4297 .ptr_from_int,4321 .ptr_from_int,
4298 .enum_from_int,4322 .enum_from_int,
4323 .backing_int,
4324 .from_backing_int,
4299 .float_cast,4325 .float_cast,
4300 .int_cast,4326 .int_cast,
4301 .ptr_cast,4327 .ptr_cast,
lib/std/zig/parser_test.zig+61-1
...@@ -1120,7 +1120,7 @@ test "zig fmt: empty enum decls" {...@@ -1120,7 +1120,7 @@ test "zig fmt: empty enum decls" {
1120 \\const A = enum {};1120 \\const A = enum {};
1121 \\const B = enum(u32) {};1121 \\const B = enum(u32) {};
1122 \\const C = extern enum(c_int) {};1122 \\const C = extern enum(c_int) {};
1123 \\const D = packed enum(u8) {};1123 \\const D = packed enum(noreturn) {};
1124 \\1124 \\
1125 );1125 );
1126}1126}
...@@ -6938,6 +6938,66 @@ test "zig fmt: inner over-indented if expressions becoming multiline" {...@@ -6938,6 +6938,66 @@ test "zig fmt: inner over-indented if expressions becoming multiline" {
6938 );6938 );
6939}6939}
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
6941test "recovery: top level" {7001test "recovery: top level" {
6942 try testError(7002 try testError(
6943 \\test "" {inline}7003 \\test "" {inline}
src/Air.zig+6
...@@ -288,6 +288,10 @@ pub const Inst = struct {...@@ -288,6 +288,10 @@ pub const Inst = struct {
288 ///288 ///
289 /// Uses the `ty_op` field.289 /// Uses the `ty_op` field.
290 bit_cast,290 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,
291 /// Cast a pointer to a different pointer type. The result type is a slice iff the operand295 /// Cast a pointer to a different pointer type. The result type is a slice iff the operand
292 /// type is a slice (the length of the slice does not change). All other pointer attributes296 /// type is a slice (the length of the slice does not change). All other pointer attributes
293 /// except for the address space may change.297 /// except for the address space may change.
...@@ -1717,6 +1721,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1717,6 +1721,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
17171721
1718 .not,1722 .not,
1719 .bit_cast,1723 .bit_cast,
1724 .bit_cast_safe,
1720 .ptr_cast,1725 .ptr_cast,
1721 .ptr_from_int,1726 .ptr_from_int,
1722 .int_from_ptr,1727 .int_from_ptr,
...@@ -1969,6 +1974,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -1969,6 +1974,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1969 .add_safe,1974 .add_safe,
1970 .sub_safe,1975 .sub_safe,
1971 .mul_safe,1976 .mul_safe,
1977 .bit_cast_safe,
1972 .int_cast_safe,1978 .int_cast_safe,
1973 .int_from_float_safe,1979 .int_from_float_safe,
1974 .int_from_float_optimized_safe,1980 .int_from_float_optimized_safe,
src/Air/Legalize.zig+116-14
...@@ -156,6 +156,10 @@ pub const Feature = enum {...@@ -156,6 +156,10 @@ pub const Feature = enum {
156 /// Legalize splat to a one element vector to a bitcast.156 /// Legalize splat to a one element vector to a bitcast.
157 splat_one_elem_to_bit_cast,157 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,
159 /// Replace `int_cast_safe` with an explicit safety check which `call`s the panic function on failure.163 /// Replace `int_cast_safe` with an explicit safety check which `call`s the panic function on failure.
160 /// Not compatible with `scalarize_int_cast_safe`.164 /// Not compatible with `scalarize_int_cast_safe`.
161 expand_int_cast_safe,165 expand_int_cast_safe,
...@@ -600,9 +604,28 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -600,9 +604,28 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
600 continue :inst l.replaceInst(inst, .block, payload);604 continue :inst l.replaceInst(inst, .block, payload);
601 }605 }
602 },606 },
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 },
603 .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) {622 .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) {
604 assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both623 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 });
606 } else if (l.features.has(.scalarize_int_cast_safe)) {629 } else if (l.features.has(.scalarize_int_cast_safe)) {
607 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;630 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
608 if (ty_op.ty.toType().isVector(zcu)) {631 if (ty_op.ty.toType().isVector(zcu)) {
...@@ -1006,7 +1029,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz...@@ -1006,7 +1029,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
10061029
1007 if (result_is_array) {1030 if (result_is_array) {
1008 // This is only allowed when legalizing an elementwise bitcast.1031 // 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 }
1010 assert(form == .ty_op);1036 assert(form == .ty_op);
1011 }1037 }
10121038
...@@ -1076,7 +1102,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz...@@ -1076,7 +1102,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
1076 orig_operand,1102 orig_operand,
1077 index_val,1103 index_val,
1078 ).toRef();1104 ).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();
1080 },1110 },
1081 .bin_op => elem: {1111 .bin_op => elem: {
1082 const orig_bin = orig.data.bin_op;1112 const orig_bin = orig.data.bin_op;
...@@ -1700,9 +1730,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?...@@ -1700,9 +1730,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?
17001730
1701 // Now convert `uint_ty` (`uN`) to `dest_ty`.1731 // 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
1703 if (int_to_dest_ok) {1737 if (int_to_dest_ok) {
1704 _ = main_block.stealCapacity(17);1738 _ = 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 };
1706 main_block.addBr(l, orig_inst, result);1750 main_block.addBr(l, orig_inst, result);
1707 } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) {1751 } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) {
1708 _ = main_block.stealCapacity(16);1752 _ = main_block.stealCapacity(16);
...@@ -2089,7 +2133,62 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize...@@ -2089,7 +2133,62 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize
2089 } };2133 } };
2090}2134}
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 {
2093 const pt = l.pt;2192 const pt = l.pt;
2094 const zcu = pt.zcu;2193 const zcu = pt.zcu;
2095 const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op;2194 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...@@ -2108,6 +2207,9 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
2108 .@"enum" => true,2207 .@"enum" => true,
2109 else => unreachable,2208 else => unreachable,
2110 };2209 };
2210 const have_enum_value_check = dest_is_enum and
2211 !dest_ty.isNonexhaustiveEnum(zcu) and
2212 zcu.backendSupportsFeature(.is_named_enum_value);
21112213
2112 const operand_info = operand_scalar_ty.intInfo(zcu);2214 const operand_info = operand_scalar_ty.intInfo(zcu);
2113 const dest_info = dest_scalar_ty.intInfo(zcu);2215 const dest_info = dest_scalar_ty.intInfo(zcu);
...@@ -2123,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In...@@ -2123,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
2123 };2225 };
2124 };2226 };
21252227
2228 if (!have_enum_value_check and !have_min_check and !have_max_check) {
2229 return null;
2230 }
2231
2126 // The worst-case scenario in terms of total instructions and total condbrs is the case where2232 // The worst-case scenario in terms of total instructions and total condbrs is the case where
2127 // the result type is an exhaustive enum whose tag type is smaller than the operand type:2233 // the result type is an exhaustive enum whose tag type is smaller than the operand type:
2128 //2234 //
...@@ -2172,7 +2278,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In...@@ -2172,7 +2278,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
2172 const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds;2278 const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds;
21732279
2174 if (have_min_check or have_max_check) {2280 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;
2176 const condbr = &condbr_buf[condbr_idx];2282 const condbr = &condbr_buf[condbr_idx];
2177 condbr_idx += 1;2283 condbr_idx += 1;
2178 const below_min_inst: Air.Inst.Index = if (have_min_check) inst: {2284 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...@@ -2218,7 +2324,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
2218 } },2324 } },
2219 });2325 });
2220 // For ints we're already done, but for exhaustive enums we must check this is a valid tag.2326 // 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) {
2222 assert(!is_vector); // vectors of enums don't exist2328 assert(!is_vector); // vectors of enums don't exist
2223 // We are building this:2329 // We are building this:
2224 // %1 = is_named_enum_value(%cast_inst)2330 // %1 = is_named_enum_value(%cast_inst)
...@@ -2240,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In...@@ -2240,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
2240 cur_block = &condbr.then_block;2346 cur_block = &condbr.then_block;
2241 }2347 }
2242 // Finally, just `br` to our outer `block`.2348 // Finally, just `br` to our outer `block`.
2243 _ = cur_block.add(l, .{2349 cur_block.addBr(l, orig_inst, cast_inst.toRef());
2244 .tag = .br,2350
2245 .data = .{ .br = .{
2246 .block_inst = orig_inst,
2247 .operand = cast_inst.toRef(),
2248 } },
2249 });
2250 // We might not have used all of the instructions; that's intentional.2351 // We might not have used all of the instructions; that's intentional.
2251 _ = cur_block.stealRemainingCapacity();2352 _ = cur_block.stealRemainingCapacity();
22522353
2354 assert(condbr_idx != 0); // should have already returned `null`
2253 for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l);2355 for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l);
2254 return .{ .ty_pl = .{2356 return .{ .ty_pl = .{
2255 .ty = Air.internedToRef(dest_ty.toIntern()),2357 .ty = Air.internedToRef(dest_ty.toIntern()),
src/Air/Liveness.zig+1
...@@ -491,6 +491,7 @@ fn analyzeInst(...@@ -491,6 +491,7 @@ fn analyzeInst(
491491
492 .not,492 .not,
493 .bit_cast,493 .bit_cast,
494 .bit_cast_safe,
494 .ptr_cast,495 .ptr_cast,
495 .ptr_from_int,496 .ptr_from_int,
496 .int_from_ptr,497 .int_from_ptr,
src/Air/Liveness/Verify.zig+1
...@@ -79,6 +79,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -79,6 +79,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
79 // unary79 // unary
80 .not,80 .not,
81 .bit_cast,81 .bit_cast,
82 .bit_cast_safe,
82 .ptr_cast,83 .ptr_cast,
83 .ptr_from_int,84 .ptr_from_int,
84 .int_from_ptr,85 .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 {...@@ -118,7 +118,7 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
118 if (ptr_ty.childType(zcu).toIntern() != verify.ret_ty.toIntern()) return verify.fail("bad return type");118 if (ptr_ty.childType(zcu).toIntern() != verify.ret_ty.toIntern()) return verify.fail("bad return type");
119 },119 },
120120
121 .bit_cast => {121 .bit_cast, .bit_cast_safe => {
122 const ty_op = data[@intFromEnum(inst)].ty_op;122 const ty_op = data[@intFromEnum(inst)].ty_op;
123 const operand_ty = air.typeOf(ty_op.operand, ip);123 const operand_ty = air.typeOf(ty_op.operand, ip);
124 const result_ty = ty_op.ty.toType();124 const result_ty = ty_op.ty.toType();
src/Air/print.zig+1
...@@ -231,6 +231,7 @@ const Writer = struct {...@@ -231,6 +231,7 @@ const Writer = struct {
231231
232 .not,232 .not,
233 .bit_cast,233 .bit_cast,
234 .bit_cast_safe,
234 .ptr_cast,235 .ptr_cast,
235 .ptr_from_int,236 .ptr_from_int,
236 .int_from_ptr,237 .int_from_ptr,
src/InternPool.zig+3-7
...@@ -5040,7 +5040,6 @@ pub const Tag = enum(u8) {...@@ -5040,7 +5040,6 @@ pub const Tag = enum(u8) {
5040 /// The set of values that are encoded this way is:5040 /// The set of values that are encoded this way is:
5041 /// * An array or vector which has length 0.5041 /// * An array or vector which has length 0.
5042 /// * A struct which has all fields comptime-known.5042 /// * 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
5044 /// data is Index of the type, which is known to be zero bits at runtime.5043 /// data is Index of the type, which is known to be zero bits at runtime.
5045 only_possible_value,5044 only_possible_value,
5046 /// data is extra index to Key.Union.5045 /// 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:...@@ -7740,12 +7739,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
7740 }),7739 }),
77417740
7742 .enum_tag => |enum_tag| {7741 .enum_tag => |enum_tag| {
7743 assert(ip.isEnumType(enum_tag.ty));7742 const enum_obj = ip.loadEnumType(enum_tag.ty);
7744 switch (ip.indexToKey(enum_tag.ty)) {7743 assert(ip.typeOf(enum_tag.int) == enum_obj.int_tag_type);
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 }
7749 items.appendAssumeCapacity(.{7744 items.appendAssumeCapacity(.{
7750 .tag = .enum_tag,7745 .tag = .enum_tag,
7751 .data = try addExtra(extra, enum_tag),7746 .data = try addExtra(extra, enum_tag),
...@@ -10197,6 +10192,7 @@ pub fn getCoerced(...@@ -10197,6 +10192,7 @@ pub fn getCoerced(
10197 .enum_type => {10192 .enum_type => {
10198 const enum_type = ip.loadEnumType(new_ty);10193 const enum_type = ip.loadEnumType(new_ty);
10199 const index = enum_type.nameIndex(ip, enum_literal).?;10194 const index = enum_type.nameIndex(ip, enum_literal).?;
10195 assert(enum_type.int_tag_type != .noreturn_type);
10200 return ip.get(gpa, io, tid, .{ .enum_tag = .{10196 return ip.get(gpa, io, tid, .{ .enum_tag = .{
10201 .ty = new_ty,10197 .ty = new_ty,
10202 .int = if (enum_type.field_values.len != 0)10198 .int = if (enum_type.field_values.len != 0)
src/Sema.zig+248-99
...@@ -1191,11 +1191,14 @@ fn analyzeBodyInner(...@@ -1191,11 +1191,14 @@ fn analyzeBodyInner(
1191 .elem_type => try sema.zirElemType(block, inst),1191 .elem_type => try sema.zirElemType(block, inst),
1192 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),1192 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
1193 .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst),1193 .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst),
1194 .from_backing_int_arg_ty => try sema.zirFromBackingIntArgTy(block, inst),
1194 .enum_literal => try sema.zirEnumLiteral(block, inst),1195 .enum_literal => try sema.zirEnumLiteral(block, inst),
1195 .decl_literal => try sema.zirDeclLiteral(block, inst, true),1196 .decl_literal => try sema.zirDeclLiteral(block, inst, true),
1196 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),1197 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
1197 .int_from_enum => try sema.zirIntFromEnum(block, inst),1198 .int_from_enum => try sema.zirIntFromEnum(block, inst),
1198 .enum_from_int => try sema.zirEnumFromInt(block, inst),1199 .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),
1199 .err_union_code => try sema.zirErrUnionCode(block, inst),1202 .err_union_code => try sema.zirErrUnionCode(block, inst),
1200 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),1203 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
1201 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst),1204 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst),
...@@ -2428,6 +2431,28 @@ fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src:...@@ -2428,6 +2431,28 @@ fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src:
2428 });2431 });
2429}2432}
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
2431fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {2456fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {
2432 const pt = sema.pt;2457 const pt = sema.pt;
2433 const msg = msg: {2458 const msg = msg: {
...@@ -4782,7 +4807,7 @@ fn failWithBadUnionFieldAccess(...@@ -4782,7 +4807,7 @@ fn failWithBadUnionFieldAccess(
4782 return sema.failWithOwnedErrorMsg(block, msg);4807 return sema.failWithOwnedErrorMsg(block, msg);
4783}4808}
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 {
4786 const zcu = sema.pt.zcu;4811 const zcu = sema.pt.zcu;
4787 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;4812 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
4788 const category = switch (decl_ty.zigTypeTag(zcu)) {4813 const category = switch (decl_ty.zigTypeTag(zcu)) {
...@@ -7853,19 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -7853,19 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
7853 },7878 },
7854 };7879 };
7855 const enum_tag_ty = sema.typeOf(enum_tag);7880 const enum_tag_ty = sema.typeOf(enum_tag);
7856 const int_tag_ty = enum_tag_ty.intTagType(zcu);7881 const int_tag_ty = enum_tag_ty.backingIntType(zcu);
78577882 assert(int_tag_ty.classify(zcu) != .no_possible_value);
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 }
78657883
7866 if (sema.resolveValue(enum_tag)) |enum_tag_val| {7884 if (sema.resolveValue(enum_tag)) |enum_tag_val| {
7867 if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty);7885 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));
7869 }7887 }
78707888
7871 try sema.requireRuntimeBlock(block, src, operand_src);7889 try sema.requireRuntimeBlock(block, src, operand_src);
...@@ -7891,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -7891,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
78917909
7892 if (sema.resolveValue(operand)) |int_val| {7910 if (sema.resolveValue(operand)) |int_val| {
7893 if (dest_ty.isNonexhaustiveEnum(zcu)) {7911 if (dest_ty.isNonexhaustiveEnum(zcu)) {
7894 const int_tag_ty = dest_ty.intTagType(zcu);7912 const int_tag_ty = dest_ty.backingIntType(zcu);
7895 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {7913 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {
7896 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());7914 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
7897 }7915 }
...@@ -7910,15 +7928,11 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -7910,15 +7928,11 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
7910 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());7928 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
7911 }7929 }
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
7917 if (try dest_ty.onePossibleValue(pt)) |opv| {7931 if (try dest_ty.onePossibleValue(pt)) |opv| {
7918 if (block.wantSafety()) {7932 if (block.wantSafety()) {
7919 // The operand is runtime-known but the result is comptime-known. In7933 // The operand is runtime-known but the result is comptime-known. In
7920 // this case we still need a safety check.7934 // 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);
7922 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));7936 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));
7923 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);7937 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
7924 }7938 }
...@@ -9372,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9372,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9372 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),9386 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),
9373 else => {},9387 else => {},
9374 }9388 }
9375
9376 break :msg msg;9389 break :msg msg;
9377 }),9390 }),
9378 .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {9391 .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...@@ -9407,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9407 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});9420 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
9408 }9421 }
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);
9411}9600}
94129601
9413fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9602fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -9940,12 +10129,8 @@ fn analyzeSwitchBlock(...@@ -9940,12 +10129,8 @@ fn analyzeSwitchBlock(
9940 operand_ty.containerLayout(zcu) != .@"packed";10129 operand_ty.containerLayout(zcu) != .@"packed";
9941 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;10130 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
994210131
9943 if (item_ty.zigTypeTag(zcu) == .@"enum" and10132 // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909)
9944 validated_switch.seen_enum_fields.len == 0 and10133 if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated
9945 !operand_ty.isNonexhaustiveEnum(zcu))
9946 {
9947 return .void_value; // switch on empty enum/union
9948 }
994910134
9950 const cond_ref = switch (operand) {10135 const cond_ref = switch (operand) {
9951 .simple => |s| s.cond,10136 .simple => |s| s.cond,
...@@ -10451,26 +10636,11 @@ fn finishSwitchBr(...@@ -10451,26 +10636,11 @@ fn finishSwitchBr(
10451 }10636 }
1045210637
10453 var prev_result_overflowed = false;10638 var prev_result_overflowed = false;
10454 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({10639 while (item.compareScalar(.lte, item_last, item_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 };
10463 assert(!prev_result_overflowed);10640 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);
10465 prev_result_overflowed = result.overflow;10642 prev_result_overflowed = result.overflow;
10466 item = switch (operand_ty.zigTypeTag(zcu)) {10643 item = result.val;
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 };
10474 }) {10644 }) {
10475 cases_len += 1;10645 cases_len += 1;
10476 case_block.instructions.clearRetainingCapacity();10646 case_block.instructions.clearRetainingCapacity();
...@@ -10603,7 +10773,7 @@ fn finishSwitchBr(...@@ -10603,7 +10773,7 @@ fn finishSwitchBr(
10603 break :check_enumerable .{ undefined, min_int };10773 break :check_enumerable .{ undefined, min_int };
10604 },10774 },
10605 .@"union", .@"struct" => {10775 .@"union", .@"struct" => {
10606 const backing_int_ty = item_ty.bitpackBackingInt(zcu);10776 const backing_int_ty = item_ty.backingIntType(zcu);
10607 const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty);10777 const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty);
10608 break :check_enumerable .{ undefined, min_backing_int };10778 break :check_enumerable .{ undefined, min_backing_int };
10609 },10779 },
...@@ -10904,7 +11074,7 @@ const ValidatedSwitchBlock = struct {...@@ -10904,7 +11074,7 @@ const ValidatedSwitchBlock = struct {
10904 var cur_val = it.next_val orelse return null;11074 var cur_val = it.next_val orelse return null;
10905 const int_ty = switch (type_tag) {11075 const int_ty = switch (type_tag) {
10906 .int => item_ty,11076 .int => item_ty,
10907 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),11077 .@"union", .@"struct" => item_ty.backingIntType(zcu),
10908 else => unreachable,11078 else => unreachable,
10909 };11079 };
10910 while (it.next_idx < it.seen_ranges.len and11080 while (it.next_idx < it.seen_ranges.len and
...@@ -11334,7 +11504,7 @@ fn validateSwitchBlock(...@@ -11334,7 +11504,7 @@ fn validateSwitchBlock(
11334 check_range: {11504 check_range: {
11335 const int_ty = switch (type_tag) {11505 const int_ty = switch (type_tag) {
11336 .int => item_ty,11506 .int => item_ty,
11337 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),11507 .@"union", .@"struct" => item_ty.backingIntType(zcu),
11338 else => unreachable,11508 else => unreachable,
11339 };11509 };
11340 const min_int = try int_ty.minInt(pt, int_ty);11510 const min_int = try int_ty.minInt(pt, int_ty);
...@@ -11956,14 +12126,7 @@ fn analyzeSwitchCaptures(...@@ -11956,14 +12126,7 @@ fn analyzeSwitchCaptures(
11956 try sema.analyzeUnreachable(case_block, operand_src, false);12126 try sema.analyzeUnreachable(case_block, operand_src, false);
11957 break :payload_ref .unreachable_value;12127 break :payload_ref .unreachable_value;
11958 };12128 };
11959 if (sema.resolveValue(loaded_operand)) |err_val| {12129 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
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 }
11967 },12130 },
11968 .item_refs => |item_refs| {12131 .item_refs => |item_refs| {
11969 var names: InferredErrorSet.NameMap = .{};12132 var names: InferredErrorSet.NameMap = .{};
...@@ -11973,14 +12136,7 @@ fn analyzeSwitchCaptures(...@@ -11973,14 +12136,7 @@ fn analyzeSwitchCaptures(
11973 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});12136 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});
11974 }12137 }
11975 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());12138 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());
11976 if (sema.resolveValue(loaded_operand)) |err_val| {12139 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
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 }
11984 },12140 },
11985 }12141 }
11986 }12142 }
...@@ -12461,7 +12617,7 @@ fn validateSwitchItemOrRange(...@@ -12461,7 +12617,7 @@ fn validateSwitchItemOrRange(
12461 .first = .fromInterned(backing_int_val),12617 .first = .fromInterned(backing_int_val),
12462 .last = .fromInterned(backing_int_val),12618 .last = .fromInterned(backing_int_val),
12463 .src = item_src,12619 .src = item_src,
12464 }, item_ty.bitpackBackingInt(zcu), zcu);12620 }, item_ty.backingIntType(zcu), zcu);
12465 },12621 },
12466 .enum_literal, .@"fn", .pointer, .type => {12622 .enum_literal, .@"fn", .pointer, .type => {
12467 break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev|12623 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...@@ -18418,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
18418 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);18574 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);
1841918575
18420 if (union_ty.containerLayout(zcu) == .@"packed") {18576 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);
18422 }18578 }
1842318579
18424 if (sema.resolveValue(payload)) |payload_val| {18580 if (sema.resolveValue(payload)) |payload_val| {
...@@ -18555,7 +18711,7 @@ fn zirStructInit(...@@ -18555,7 +18711,7 @@ fn zirStructInit(
18555 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);18711 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
1855618712
18557 if (resolved_ty.containerLayout(zcu) == .@"packed") {18713 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);
18559 const result_val = try sema.coerce(block, result_ty, union_val, src);18715 const result_val = try sema.coerce(block, result_ty, union_val, src);
18560 if (is_ref) {18716 if (is_ref) {
18561 return sema.analyzeRef(block, src, result_val, .none);18717 return sema.analyzeRef(block, src, result_val, .none);
...@@ -19567,14 +19723,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19567,14 +19723,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19567 operand_ty.fmt(pt),19723 operand_ty.fmt(pt),
19568 }),19724 }),
19569 };19725 };
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 }
19578 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);19726 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
19579 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {19727 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
19580 const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse {19728 const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse {
...@@ -29742,41 +29890,29 @@ fn storePtrVal(...@@ -29742,41 +29890,29 @@ fn storePtrVal(
29742 }29890 }
29743}29891}
2974429892
29745fn bitCast(29893/// Asserts that the layout of `dest_ty` is already resolved.
29894fn bitCastUnchecked(
29746 sema: *Sema,29895 sema: *Sema,
29747 block: *Block,29896 block: *Block,
29748 dest_ty: Type,29897 dest_ty: Type,
29749 inst: Air.Inst.Ref,29898 inst: Air.Inst.Ref,
29750 inst_src: LazySrcLoc,
29751) CompileError!Air.Inst.Ref {29899) CompileError!Air.Inst.Ref {
29752 const pt = sema.pt;29900 const zcu = sema.pt.zcu;
29753 const zcu = pt.zcu;
29754 const old_ty = sema.typeOf(inst);29901 const old_ty = sema.typeOf(inst);
2975529902
29756 old_ty.assertHasLayout(zcu);29903 old_ty.assertHasLayout(zcu);
29757 try sema.ensureLayoutResolved(dest_ty, inst_src, .init);29904 dest_ty.assertHasLayout(zcu);
2975829905
29759 assert(old_ty.hasBitRepresentation(zcu));29906 assert(old_ty.hasBitRepresentation(zcu));
29760 assert(dest_ty.hasBitRepresentation(zcu));29907 assert(dest_ty.hasBitRepresentation(zcu));
29761 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);29908 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
29762 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);29909 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
2976329910 assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu));
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 }
2977529911
29776 if (sema.resolveValue(inst)) |val| {29912 if (sema.resolveValue(inst)) |val| {
29777 return .fromValue(try sema.bitCastVal(val, dest_ty));29913 return .fromValue(try sema.bitCastVal(val, dest_ty));
29778 }29914 }
29779 try sema.validateRuntimeValue(block, inst_src, inst);29915
29780 return block.addTyOp(.bit_cast, dest_ty, inst);29916 return block.addTyOp(.bit_cast, dest_ty, inst);
29781}29917}
2978229918
...@@ -29800,6 +29936,26 @@ pub fn bitCastVal(...@@ -29800,6 +29936,26 @@ pub fn bitCastVal(
29800 }29936 }
29801}29937}
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
29803fn checkSpirvSliceAllowed(29959fn checkSpirvSliceAllowed(
29804 sema: *Sema,29960 sema: *Sema,
29805 block: *Block,29961 block: *Block,
...@@ -33968,14 +34124,6 @@ fn intFromFloatScalar(...@@ -33968,14 +34124,6 @@ fn intFromFloatScalar(
33968 return pt.getCoerced(cti_result, int_ty);34124 return pt.getCoerced(cti_result, int_ty);
33969}34125}
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
33979/// Asserts the type is an exhaustive enum.34127/// Asserts the type is an exhaustive enum.
33980fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {34128fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
33981 const pt = sema.pt;34129 const pt = sema.pt;
...@@ -33985,6 +34133,7 @@ fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {...@@ -33985,6 +34133,7 @@ fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
33985 // The `tagValueIndex` function call below relies on the type being the integer tag type.34133 // The `tagValueIndex` function call below relies on the type being the integer tag type.
33986 // `getCoerced` assumes the value will fit the new type.34134 // `getCoerced` assumes the value will fit the new type.
33987 const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type);34135 const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type);
34136 if (int_tag_ty.classify(zcu) == .no_possible_value) return false;
33988 if (!int.intFitsInType(int_tag_ty, null, zcu)) return false;34137 if (!int.intFitsInType(int_tag_ty, null, zcu)) return false;
33989 const int_coerced = try pt.getCoerced(int, int_tag_ty);34138 const int_coerced = try pt.getCoerced(int, int_tag_ty);
33990 return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;34139 return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;
src/Sema/reinterpret.zig+4-4
...@@ -353,7 +353,7 @@ const PackValueBytes = struct {...@@ -353,7 +353,7 @@ const PackValueBytes = struct {
353 return pt.aggregateValue(ty, elems);353 return pt.aggregateValue(ty, elems);
354 },354 },
355 .@"packed" => {355 .@"packed" => {
356 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));356 const backing_int_val = try pack.primitive(ty.backingIntType(zcu));
357 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);357 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);
358 return pt.bitpackValue(ty, backing_int_val);358 return pt.bitpackValue(ty, backing_int_val);
359 },359 },
...@@ -424,15 +424,15 @@ const PackValueBytes = struct {...@@ -424,15 +424,15 @@ const PackValueBytes = struct {
424 }424 }
425 },425 },
426 .@"packed" => {426 .@"packed" => {
427 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));427 const backing_int_val = try pack.primitive(ty.backingIntType(zcu));
428 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);428 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);
429 return pt.bitpackValue(ty, backing_int_val);429 return pt.bitpackValue(ty, backing_int_val);
430 },430 },
431 },431 },
432 .@"enum" => {432 .@"enum" => {
433 const tag_int_val = try pack.primitive(ty.intTagType(zcu));433 const tag_int_val = try pack.primitive(ty.backingIntType(zcu));
434 if (tag_int_val.isUndef(zcu)) return pt.undefValue(ty);434 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);
436 },436 },
437 else => return pack.primitive(ty),437 else => return pack.primitive(ty),
438 }438 }
src/Sema/type_resolution.zig+23-7
...@@ -63,7 +63,7 @@ pub const LayoutResolveReason = enum {...@@ -63,7 +63,7 @@ pub const LayoutResolveReason = enum {
63 .@"export" => "for export here",63 .@"export" => "for export here",
64 .@"extern" => "for extern declaration here",64 .@"extern" => "for extern declaration here",
65 .asm_out_type => "for inline assembly output type declared here",65 .asm_out_type => "for inline assembly output type declared here",
66 .std_lang_type => "from 'std.lang'",66 .std_lang_type => "from 'std.lang'",
67 // zig fmt: on67 // zig fmt: on
68 };68 };
69 }69 }
...@@ -1325,15 +1325,31 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void {...@@ -1325,15 +1325,31 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void {
1325 const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index);1325 const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index);
1326 break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref);1326 break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref);
1327 };1327 };
1328 const empty_exhaustive = enum_obj.field_names.len == 0 and !enum_obj.nonexhaustive;
1328 const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: {1329 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 switch (int_tag_ty.zigTypeTag(zcu)) {
1330 &block,1331 .int => if (empty_exhaustive) return sema.fail(
1331 block.src(.container_arg),1332 &block,
1332 "expected integer tag type, found '{f}'",1333 block.src(.container_arg),
1333 .{int_tag_ty.fmt(pt)},1334 "empty exhaustive enums must be backed by 'noreturn'",
1334 );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 }
1335 break :ty int_tag_ty;1350 break :ty int_tag_ty;
1336 } else ty: {1351 } else ty: {
1352 if (empty_exhaustive) break :ty .noreturn;
1337 // Infer the int tag type from the field count1353 // Infer the int tag type from the field count
1338 const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1);1354 const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1);
1339 break :ty try pt.intType(.unsigned, bits);1355 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...@@ -1580,15 +1580,28 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.lang.Type.ContainerLayout
1580 };1580 };
1581}1581}
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 {
1584 const ip = &zcu.intern_pool;1585 const ip = &zcu.intern_pool;
1585 return switch (ip.indexToKey(ty.toIntern())) {1586 return switch (ip.indexToKey(ty.toIntern())) {
1587 .enum_type => .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
1586 .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),1588 .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
1587 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),1589 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
1588 else => unreachable,1590 else => unreachable,
1589 };1591 };
1590}1592}
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
1592/// Asserts that the type is an error union.1605/// Asserts that the type is an error union.
1593pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {1606pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
1594 return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);1607 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 {...@@ -2093,11 +2106,8 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {
2093 return try pt.unionValue(ty, tag_val, payload_val);2106 return try pt.unionValue(ty, tag_val, payload_val);
2094 } else unreachable;2107 } else unreachable;
2095 },2108 },
2096 .enum_type => if (try ty.intTagType(zcu).onePossibleValue(pt)) |int_tag_opv| {2109 .enum_type => if (try ty.backingIntType(zcu).onePossibleValue(pt)) |int_tag_opv| {
2097 return .fromInterned(try pt.intern(.{ .enum_tag = .{2110 return try pt.enumValue(ty, int_tag_opv);
2098 .ty = ty.toIntern(),
2099 .int = int_tag_opv.toIntern(),
2100 } }));
2101 } else null,2111 } else null,
21022112
2103 // values, not types2113 // values, not types
...@@ -2274,17 +2284,6 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {...@@ -2274,17 +2284,6 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
2274 return pt.intValue_big(dest_ty, res.toConst());2284 return pt.intValue_big(dest_ty, res.toConst());
2275}2285}
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
2288pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {2287pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {
2289 const ip = &zcu.intern_pool;2288 const ip = &zcu.intern_pool;
2290 return switch (ip.indexToKey(ty.toIntern())) {2289 return switch (ip.indexToKey(ty.toIntern())) {
...@@ -3058,8 +3057,11 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {...@@ -3058,8 +3057,11 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
3058 .one, .many, .c => .pointer,3057 .one, .many, .c => .pointer,
3059 },3058 },
30603059
3061 .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode) {3060 .@"enum" => switch (ty.backingIntMode(zcu)) {
3062 .explicit => null,3061 .explicit => switch (ty.backingIntType(zcu).toIntern()) {
3062 else => null,
3063 .noreturn_type => .other,
3064 },
3063 .auto => .{ .enum_inferred_int_tag = ty },3065 .auto => .{ .enum_inferred_int_tag = ty },
3064 },3066 },
30653067
...@@ -3225,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {...@@ -3225,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {
3225 .float,3227 .float,
3226 => true,3228 => 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 },
3229 .pointer, .optional => ty.isPtrAtRuntime(zcu),3235 .pointer, .optional => ty.isPtrAtRuntime(zcu),
3230 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",3236 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
32313237
src/Value.zig+11-6
...@@ -146,8 +146,13 @@ pub fn toType(self: Value) Type {...@@ -146,8 +146,13 @@ pub fn toType(self: Value) Type {
146 return Type.fromInterned(self.toIntern());146 return Type.fromInterned(self.toIntern());
147}147}
148148
149pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {149/// Asserts that value is defined and of enum or bitpack type.
150 return .fromInterned(zcu.intern_pool.indexToKey(val.toIntern()).enum_tag.int);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 };
151}156}
152157
153/// Asserts that `val` is an integer.158/// Asserts that `val` is an integer.
...@@ -415,7 +420,7 @@ pub fn writeToPackedMemory(...@@ -415,7 +420,7 @@ pub fn writeToPackedMemory(
415 }420 }
416 },421 },
417 .@"enum" => {422 .@"enum" => {
418 const int_val = val.intFromEnum(zcu);423 const int_val = val.backingInt(zcu);
419 int_val.writeToPackedMemory(zcu, buffer, bit_offset);424 int_val.writeToPackedMemory(zcu, buffer, bit_offset);
420 },425 },
421 .int => {426 .int => {
...@@ -564,7 +569,7 @@ pub fn readFromPackedMemory(...@@ -564,7 +569,7 @@ pub fn readFromPackedMemory(
564 return pt.intValue_big(ty, bigint.toConst());569 return pt.intValue_big(ty, bigint.toConst());
565 },570 },
566 .@"enum" => {571 .@"enum" => {
567 const int_ty = ty.intTagType(zcu);572 const int_ty = ty.backingIntType(zcu);
568 const int_val: Value = try .readFromPackedMemory(int_ty, pt, buffer, bit_offset);573 const int_val: Value = try .readFromPackedMemory(int_ty, pt, buffer, bit_offset);
569 return pt.getCoerced(int_val, ty);574 return pt.getCoerced(int_val, ty);
570 },575 },
...@@ -581,7 +586,7 @@ pub fn readFromPackedMemory(...@@ -581,7 +586,7 @@ pub fn readFromPackedMemory(
581 } })),586 } })),
582 .@"struct", .@"union" => {587 .@"struct", .@"union" => {
583 assert(ty.containerLayout(zcu) == .@"packed");588 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);
585 return pt.bitpackValue(ty, int_val);590 return pt.bitpackValue(ty, int_val);
586 },591 },
587 .array, .vector => {592 .array, .vector => {
...@@ -2368,7 +2373,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory...@@ -2368,7 +2373,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
2368 try pt.nullValue(ty),2373 try pt.nullValue(ty),
23692374
2370 .@"enum" => switch (interpret_mode) {2375 .@"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)),
2372 .by_name => {2377 .by_name => {
2373 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls);2378 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls);
2374 const field_idx = ty.enumFieldIndex(field_name_ip, zcu) orelse return error.TypeMismatch;2379 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...@@ -3956,7 +3956,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
3956}3956}
39573957
3958pub fn intType(pt: Zcu.PerThread, signedness: std.lang.Signedness, bits: u16) Allocator.Error!Type {3958pub 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 = .{
3960 .signedness = signedness,3960 .signedness = signedness,
3961 .bits = bits,3961 .bits = bits,
3962 } }));3962 } }));
...@@ -3967,15 +3967,15 @@ pub fn errorIntType(pt: Zcu.PerThread) std.mem.Allocator.Error!Type {...@@ -3967,15 +3967,15 @@ pub fn errorIntType(pt: Zcu.PerThread) std.mem.Allocator.Error!Type {
3967}3967}
39683968
3969pub fn arrayType(pt: Zcu.PerThread, info: InternPool.Key.ArrayType) Allocator.Error!Type {3969pub 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 }));
3971}3971}
39723972
3973pub fn vectorType(pt: Zcu.PerThread, info: InternPool.Key.VectorType) Allocator.Error!Type {3973pub 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 }));
3975}3975}
39763976
3977pub fn optionalType(pt: Zcu.PerThread, child_type: InternPool.Index) Allocator.Error!Type {3977pub 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 }));
3979}3979}
39803980
3981pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!Type {3981pub 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!...@@ -3997,7 +3997,7 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!
3997 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),3997 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
3998 }3998 }
39993999
4000 return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info }));4000 return .fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
4001}4001}
40024002
4003pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {4003pub 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...@@ -4037,11 +4037,11 @@ pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Err
4037/// Use this for `anyframe->T` only.4037/// Use this for `anyframe->T` only.
4038/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.4038/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
4039pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {4039pub 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() }));
4041}4041}
40424042
4043pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {4043pub 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 = .{
4045 .error_set_type = error_set_ty.toIntern(),4045 .error_set_type = error_set_ty.toIntern(),
4046 .payload_type = payload_ty.toIntern(),4046 .payload_type = payload_ty.toIntern(),
4047 } }));4047 } }));
...@@ -4050,7 +4050,7 @@ pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) A...@@ -4050,7 +4050,7 @@ pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) A
4050pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type {4050pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type {
4051 const names: *const [1]InternPool.NullTerminatedString = &name;4051 const names: *const [1]InternPool.NullTerminatedString = &name;
4052 const comp = pt.zcu.comp;4052 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));
4054}4054}
40554055
4056/// Sorts `names` in place.4056/// Sorts `names` in place.
...@@ -4066,7 +4066,7 @@ pub fn errorSetFromUnsortedNames(...@@ -4066,7 +4066,7 @@ pub fn errorSetFromUnsortedNames(
4066 );4066 );
4067 const comp = pt.zcu.comp;4067 const comp = pt.zcu.comp;
4068 const new_ty = try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names);4068 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);
4070}4070}
40714071
4072/// Supports only pointers, not pointer-like optionals.4072/// Supports only pointers, not pointer-like optionals.
...@@ -4074,7 +4074,7 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {...@@ -4074,7 +4074,7 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
4074 const zcu = pt.zcu;4074 const zcu = pt.zcu;
4075 assert(ty.zigTypeTag(zcu) == .pointer and !ty.isSlice(zcu));4075 assert(ty.zigTypeTag(zcu) == .pointer and !ty.isSlice(zcu));
4076 assert(x != 0 or ty.isAllowzeroPtr(zcu));4076 assert(x != 0 or ty.isAllowzeroPtr(zcu));
4077 return Value.fromInterned(try pt.intern(.{ .ptr = .{4077 return .fromInterned(try pt.intern(.{ .ptr = .{
4078 .ty = ty.toIntern(),4078 .ty = ty.toIntern(),
4079 .base_addr = .int,4079 .base_addr = .int,
4080 .byte_offset = x,4080 .byte_offset = x,
...@@ -4082,14 +4082,11 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {...@@ -4082,14 +4082,11 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
4082}4082}
40834083
4084/// Creates an enum tag value based on the integer tag value.4084/// 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 {4085pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: Value) Allocator.Error!Value {
4086 if (std.debug.runtime_safety) {4086 if (std.debug.runtime_safety) assert(ty.zigTypeTag(pt.zcu) == .@"enum");
4087 const tag = ty.zigTypeTag(pt.zcu);4087 return .fromInterned(try pt.intern(.{ .enum_tag = .{
4088 assert(tag == .@"enum");
4089 }
4090 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
4091 .ty = ty.toIntern(),4088 .ty = ty.toIntern(),
4092 .int = tag_int,4089 .int = tag_int.toIntern(),
4093 } }));4090 } }));
4094}4091}
40954092
...@@ -4103,7 +4100,7 @@ pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Alloca...@@ -4103,7 +4100,7 @@ pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Alloca
41034100
4104 if (enum_type.field_values.len == 0) {4101 if (enum_type.field_values.len == 0) {
4105 // Auto-numbered fields.4102 // Auto-numbered fields.
4106 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{4103 return .fromInterned(try pt.intern(.{ .enum_tag = .{
4107 .ty = ty.toIntern(),4104 .ty = ty.toIntern(),
4108 .int = try pt.intern(.{ .int = .{4105 .int = try pt.intern(.{ .int = .{
4109 .ty = enum_type.int_tag_type,4106 .ty = enum_type.int_tag_type,
...@@ -4262,7 +4259,7 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value...@@ -4262,7 +4259,7 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value
42624259
4263/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value.4260/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value.
4264pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value {4261pub 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());
4266 return .fromInterned(try pt.intern(.{ .bitpack = .{4263 return .fromInterned(try pt.intern(.{ .bitpack = .{
4267 .ty = ty.toIntern(),4264 .ty = ty.toIntern(),
4268 .backing_int_val = backing_int_val.toIntern(),4265 .backing_int_val = backing_int_val.toIntern(),
src/codegen.zig+1-1
...@@ -415,7 +415,7 @@ pub fn generateSymbol(...@@ -415,7 +415,7 @@ pub fn generateSymbol(
415 }415 }
416 },416 },
417 .enum_tag => |enum_tag| {417 .enum_tag => |enum_tag| {
418 const int_tag_ty = ty.intTagType(zcu);418 const int_tag_ty = ty.backingIntType(zcu);
419 try generateSymbol(bin_file, pt, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent);419 try generateSymbol(bin_file, pt, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent);
420 },420 },
421 .float => |float| storage: switch (float.storage) {421 .float => |float| storage: switch (float.storage) {
src/codegen/aarch64.zig+4-2
...@@ -5,8 +5,10 @@ pub const encoding = @import("aarch64/encoding.zig");...@@ -5,8 +5,10 @@ pub const encoding = @import("aarch64/encoding.zig");
5pub const Mir = @import("aarch64/Mir.zig");5pub const Mir = @import("aarch64/Mir.zig");
6pub const Select = @import("aarch64/Select.zig");6pub const Select = @import("aarch64/Select.zig");
77
8pub fn legalizeFeatures(_: *const std.Target) ?*Air.Legalize.Features {8pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
9 return null;9 return comptime &.initMany(&.{
10 .expand_bit_cast_safe,
11 });
10}12}
1113
12pub fn generate(14pub fn generate(
src/codegen/aarch64/Select.zig+2
...@@ -362,6 +362,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -362,6 +362,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
362 air_inst_index = air_body[air_body_index];362 air_inst_index = air_body[air_body_index];
363 continue :air_tag air_tags[@intFromEnum(air_inst_index)];363 continue :air_tag air_tags[@intFromEnum(air_inst_index)];
364 },364 },
365 .bit_cast_safe => unreachable, // legalized
365 inline .block, .dbg_inline_block => |air_tag| {366 inline .block, .dbg_inline_block => |air_tag| {
366 const air_body_block = switch (air_tag) {367 const air_body_block = switch (air_tag) {
367 else => comptime unreachable,368 else => comptime unreachable,
...@@ -3201,6 +3202,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,...@@ -3201,6 +3202,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
3201 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;3202 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
3202 },3203 },
3203 .bit_cast,3204 .bit_cast,
3205 .bit_cast_safe, // TODO safety check
3204 .ptr_cast,3206 .ptr_cast,
3205 .ptr_from_int,3207 .ptr_from_int,
3206 .int_from_ptr,3208 .int_from_ptr,
src/codegen/c.zig+4-2
...@@ -27,6 +27,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {...@@ -27,6 +27,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
27 return comptime switch (dev.env.supports(.legalize)) {27 return comptime switch (dev.env.supports(.legalize)) {
28 inline false, true => |supports_legalize| &.init(.{28 inline false, true => |supports_legalize| &.init(.{
29 // we don't currently ask zig1 to use safe optimization modes29 // we don't currently ask zig1 to use safe optimization modes
30 .expand_bit_cast_safe = supports_legalize,
30 .expand_int_cast_safe = supports_legalize,31 .expand_int_cast_safe = supports_legalize,
31 .expand_int_from_float_safe = supports_legalize,32 .expand_int_from_float_safe = supports_legalize,
32 .expand_int_from_float_optimized_safe = supports_legalize,33 .expand_int_from_float_optimized_safe = supports_legalize,
...@@ -1460,7 +1461,7 @@ pub const DeclGen = struct {...@@ -1460,7 +1461,7 @@ pub const DeclGen = struct {
1460 }1461 }
1461 return w.writeByte('}');1462 return w.writeByte('}');
1462 },1463 },
1463 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),1464 .@"packed" => return dg.renderUndefValue(w, ty.backingIntType(zcu), location),
1464 }1465 }
1465 },1466 },
1466 .tuple_type => |tuple_info| {1467 .tuple_type => |tuple_info| {
...@@ -1520,7 +1521,7 @@ pub const DeclGen = struct {...@@ -1520,7 +1521,7 @@ pub const DeclGen = struct {
1520 if (loaded_union.has_runtime_tag) try w.writeByte(' ');1521 if (loaded_union.has_runtime_tag) try w.writeByte(' ');
1521 if (loaded_union.layout == .auto) try w.writeByte('}');1522 if (loaded_union.layout == .auto) try w.writeByte('}');
1522 },1523 },
1523 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),1524 .@"packed" => return dg.renderUndefValue(w, ty.backingIntType(zcu), location),
1524 }1525 }
1525 },1526 },
1526 .error_union_type => |error_union| {1527 .error_union_type => |error_union| {
...@@ -2876,6 +2877,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -2876,6 +2877,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2876 .add_safe,2877 .add_safe,
2877 .sub_safe,2878 .sub_safe,
2878 .mul_safe,2879 .mul_safe,
2880 .bit_cast_safe,
2879 .int_cast_safe,2881 .int_cast_safe,
2880 .int_from_float_safe,2882 .int_from_float_safe,
2881 .int_from_float_optimized_safe,2883 .int_from_float_optimized_safe,
src/codegen/c/type.zig+1-2
...@@ -484,8 +484,7 @@ pub const CType = union(enum) {...@@ -484,8 +484,7 @@ pub const CType = union(enum) {
484 pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass {484 pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass {
485 const int_ty: Type = switch (ty.zigTypeTag(zcu)) {485 const int_ty: Type = switch (ty.zigTypeTag(zcu)) {
486 .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu),486 .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu),
487 .@"enum" => ty.intTagType(zcu),487 .@"enum", .@"struct", .@"union" => ty.backingIntType(zcu),
488 .@"struct", .@"union" => ty.bitpackBackingInt(zcu),
489 .int => ty,488 .int => ty,
490 else => unreachable,489 else => unreachable,
491 };490 };
src/codegen/c/type/render_defs.zig+2-2
...@@ -212,7 +212,7 @@ pub fn defineComplete(...@@ -212,7 +212,7 @@ pub fn defineComplete(
212 },212 },
213 .@"enum" => {213 .@"enum" => {
214 const name_cty: CType = .{ .@"enum" = ty };214 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);
216 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{216 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
217 cty.fmtDeclaratorPrefix(zcu),217 cty.fmtDeclaratorPrefix(zcu),
218 name_cty.fmtTypeName(zcu),218 name_cty.fmtTypeName(zcu),
...@@ -343,7 +343,7 @@ fn defineBitpack(...@@ -343,7 +343,7 @@ fn defineBitpack(
343) (Allocator.Error || Writer.Error)!void {343) (Allocator.Error || Writer.Error)!void {
344 const zcu = pt.zcu;344 const zcu = pt.zcu;
345 const name_cty: CType = .{ .bitpack = ty };345 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);
347 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{347 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
348 cty.fmtDeclaratorPrefix(zcu),348 cty.fmtDeclaratorPrefix(zcu),
349 name_cty.fmtTypeName(zcu),349 name_cty.fmtTypeName(zcu),
src/codegen/llvm.zig+1-1
...@@ -3252,7 +3252,7 @@ pub const Object = struct {...@@ -3252,7 +3252,7 @@ pub const Object = struct {
3252 return ty;3252 return ty;
3253 },3253 },
3254 .opaque_type, .spirv_type => unreachable, // no runtime bits3254 .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),
3256 .func_type => |func_type| try o.lowerFnType(t, func_type),3256 .func_type => |func_type| try o.lowerFnType(t, func_type),
3257 .error_set_type, .inferred_error_set_type => try o.errorIntType(repr),3257 .error_set_type, .inferred_error_set_type => try o.errorIntType(repr),
3258 // values, not types3258 // 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...@@ -465,7 +465,8 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
465 .alloc => try self.airAlloc(inst),465 .alloc => try self.airAlloc(inst),
466 .ret_ptr => try self.airRetPtr(inst),466 .ret_ptr => try self.airRetPtr(inst),
467 .arg => try self.airArg(inst),467 .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),
469 .ptr_cast => try self.airNopCast(inst),470 .ptr_cast => try self.airNopCast(inst),
470 .ptr_from_int => try self.airPtrFromInt(inst),471 .ptr_from_int => try self.airPtrFromInt(inst),
471 .int_from_ptr => try self.airIntFromPtr(inst),472 .int_from_ptr => try self.airIntFromPtr(inst),
...@@ -1254,7 +1255,6 @@ fn cmp(...@@ -1254,7 +1255,6 @@ fn cmp(
1254 const zcu = o.zcu;1255 const zcu = o.zcu;
1255 const scalar_ty = operand_ty.scalarType(zcu);1256 const scalar_ty = operand_ty.scalarType(zcu);
1256 const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {1257 const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
1257 .@"enum" => scalar_ty.intTagType(zcu),
1258 .int, .bool, .pointer, .error_set => scalar_ty,1258 .int, .bool, .pointer, .error_set => scalar_ty,
1259 .optional => blk: {1259 .optional => blk: {
1260 const payload_ty = operand_ty.optionalChild(zcu);1260 const payload_ty = operand_ty.optionalChild(zcu);
...@@ -1328,7 +1328,7 @@ fn cmp(...@@ -1328,7 +1328,7 @@ fn cmp(
1328 return phi.toValue();1328 return phi.toValue();
1329 },1329 },
1330 .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),1330 .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),
1332 else => unreachable,1332 else => unreachable,
1333 };1333 };
1334 const is_signed = int_ty.isSignedInt(zcu);1334 const is_signed = int_ty.isSignedInt(zcu);
...@@ -4539,7 +4539,7 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value...@@ -4539,7 +4539,7 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
4539 }4539 }
4540}4540}
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 {
4543 const o = fg.object;4543 const o = fg.object;
4544 const zcu = o.zcu;4544 const zcu = o.zcu;
45454545
...@@ -4564,7 +4564,26 @@ fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value...@@ -4564,7 +4564,26 @@ fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
4564 assert(!isByRef(dest_ty, zcu));4564 assert(!isByRef(dest_ty, zcu));
45654565
4566 const llvm_dest_ty = try o.lowerType(dest_ty, .by_value);4566 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;
4568}4587}
45694588
4570fn airNopCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {4589fn 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};...@@ -51,6 +51,7 @@ const InnerError = codegen.Error || error{OutOfRegisters};
5151
52pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {52pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
53 return comptime &.initMany(&.{53 return comptime &.initMany(&.{
54 .expand_bit_cast_safe,
54 .expand_int_cast_safe,55 .expand_int_cast_safe,
55 .expand_int_from_float_safe,56 .expand_int_from_float_safe,
56 .expand_int_from_float_optimized_safe,57 .expand_int_from_float_optimized_safe,
...@@ -1454,6 +1455,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1454,6 +1455,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1454 .add_safe,1455 .add_safe,
1455 .sub_safe,1456 .sub_safe,
1456 .mul_safe,1457 .mul_safe,
1458 .bit_cast_safe,
1457 .int_cast_safe,1459 .int_cast_safe,
1458 .int_from_float_safe,1460 .int_from_float_safe,
1459 .int_from_float_optimized_safe,1461 .int_from_float_optimized_safe,
...@@ -5128,7 +5130,6 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {...@@ -5128,7 +5130,6 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
5128 .@"struct",5130 .@"struct",
5129 => {5131 => {
5130 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {5132 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
5131 .@"enum" => lhs_ty.intTagType(zcu),
5132 .int => lhs_ty,5133 .int => lhs_ty,
5133 .bool => .u1,5134 .bool => .u1,
5134 .pointer => .u64,5135 .pointer => .u64,
...@@ -5143,7 +5144,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {...@@ -5143,7 +5144,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
5143 return func.fail("TODO riscv cmp non-pointer optionals", .{});5144 return func.fail("TODO riscv cmp non-pointer optionals", .{});
5144 }5145 }
5145 },5146 },
5146 .@"struct", .@"union" => lhs_ty.bitpackBackingInt(zcu),5147 .@"enum", .@"struct", .@"union" => lhs_ty.backingIntType(zcu),
5147 else => unreachable,5148 else => unreachable,
5148 };5149 };
51495150
src/codegen/sparc64/CodeGen.zig+2-1
...@@ -697,6 +697,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -697,6 +697,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
697 .add_safe,697 .add_safe,
698 .sub_safe,698 .sub_safe,
699 .mul_safe,699 .mul_safe,
700 .bit_cast_safe,
700 .int_cast_safe,701 .int_cast_safe,
701 .int_from_float_safe,702 .int_from_float_safe,
702 .int_from_float_optimized_safe,703 .int_from_float_optimized_safe,
...@@ -1374,7 +1375,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -1374,7 +1375,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
13741375
1375 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {1376 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
1376 .vector => unreachable, // Handled by cmp_vector.1377 .vector => unreachable, // Handled by cmp_vector.
1377 .@"enum" => lhs_ty.intTagType(zcu),1378 .@"enum" => lhs_ty.backingIntType(zcu),
1378 .int => lhs_ty,1379 .int => lhs_ty,
1379 .bool => .u1,1380 .bool => .u1,
1380 .pointer => .usize,1381 .pointer => .usize,
src/codegen/spirv/CodeGen.zig+11-10
...@@ -121,6 +121,7 @@ const StructType = struct {...@@ -121,6 +121,7 @@ const StructType = struct {
121121
122pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {122pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
123 return comptime &.initMany(&.{123 return comptime &.initMany(&.{
124 .expand_bit_cast_safe,
124 .expand_int_cast_safe,125 .expand_int_cast_safe,
125 .expand_int_from_float_safe,126 .expand_int_from_float_safe,
126 .expand_int_from_float_optimized_safe,127 .expand_int_from_float_optimized_safe,
...@@ -1353,7 +1354,7 @@ fn arithmeticTypeInfo(cg: *CodeGen, ty: Type) ArithmeticTypeInfo {...@@ -1353,7 +1354,7 @@ fn arithmeticTypeInfo(cg: *CodeGen, ty: Type) ArithmeticTypeInfo {
1353 const target = cg.zcu.getTarget();1354 const target = cg.zcu.getTarget();
1354 var scalar_ty = ty.scalarType(zcu);1355 var scalar_ty = ty.scalarType(zcu);
1355 if (scalar_ty.zigTypeTag(zcu) == .@"enum") {1356 if (scalar_ty.zigTypeTag(zcu) == .@"enum") {
1356 scalar_ty = scalar_ty.intTagType(zcu);1357 scalar_ty = scalar_ty.backingIntType(zcu);
1357 }1358 }
1358 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;1359 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;
1359 return switch (scalar_ty.zigTypeTag(zcu)) {1360 return switch (scalar_ty.zigTypeTag(zcu)) {
...@@ -1732,8 +1733,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {...@@ -1732,8 +1733,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
1732 return try cg.constructComposite(comp_ty_id, &constituents);1733 return try cg.constructComposite(comp_ty_id, &constituents);
1733 },1734 },
1734 .enum_tag => {1735 .enum_tag => {
1735 const int_val = val.intFromEnum(zcu);1736 const int_val = val.backingInt(zcu);
1736 const int_ty = ty.intTagType(zcu);1737 const int_ty = ty.backingIntType(zcu);
1737 break :cache try cg.constant(int_ty, int_val, repr);1738 break :cache try cg.constant(int_ty, int_val, repr);
1738 },1739 },
1739 .ptr => return cg.constantPtr(val),1740 .ptr => return cg.constantPtr(val),
...@@ -2213,7 +2214,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {...@@ -2213,7 +2214,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
2213 const int_info = ty.intInfo(zcu);2214 const int_info = ty.intInfo(zcu);
2214 return try cg.intType(int_info.signedness, int_info.bits);2215 return try cg.intType(int_info.signedness, int_info.bits);
2215 },2216 },
2216 .@"enum" => return try cg.resolveType(ty.intTagType(zcu), repr),2217 .@"enum" => return try cg.resolveType(ty.backingIntType(zcu), repr),
2217 .float => {2218 .float => {
2218 const bits = ty.floatBits(target);2219 const bits = ty.floatBits(target);
2219 const supported = switch (bits) {2220 const supported = switch (bits) {
...@@ -5807,7 +5808,7 @@ fn cmp(...@@ -5807,7 +5808,7 @@ fn cmp(
5807 .int, .bool, .float => {},5808 .int, .bool, .float => {},
5808 .@"enum" => {5809 .@"enum" => {
5809 assert(!is_vector);5810 assert(!is_vector);
5810 const ty = lhs.ty.intTagType(zcu);5811 const ty = lhs.ty.backingIntType(zcu);
5811 return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty));5812 return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty));
5812 },5813 },
5813 .@"struct" => {5814 .@"struct" => {
...@@ -6887,7 +6888,7 @@ fn unionInit(...@@ -6887,7 +6888,7 @@ fn unionInit(
68876888
6888 const tag_int = if (layout.tag_size != 0) blk: {6889 const tag_int = if (layout.tag_size != 0) blk: {
6889 const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field);6890 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);
6891 break :blk tag_int_val.toUnsignedInt(zcu);6892 break :blk tag_int_val.toUnsignedInt(zcu);
6892 } else 0;6893 } else 0;
68936894
...@@ -8164,7 +8165,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {...@@ -8164,7 +8165,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
8164 break :blk if (backing_bits <= 32) 1 else 2;8165 break :blk if (backing_bits <= 32) 1 else 2;
8165 },8166 },
8166 .@"enum" => blk: {8167 .@"enum" => blk: {
8167 const int_ty = cond_ty.intTagType(zcu);8168 const int_ty = cond_ty.backingIntType(zcu);
8168 const int_info = int_ty.intInfo(zcu);8169 const int_info = int_ty.intInfo(zcu);
8169 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);8170 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);
8170 if (big_int) return cg.todo("implement composite int switch", .{});8171 if (big_int) return cg.todo("implement composite int switch", .{});
...@@ -8224,7 +8225,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {...@@ -8224,7 +8225,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
8224 const value: Value = .fromInterned(item.toInterned().?);8225 const value: Value = .fromInterned(item.toInterned().?);
8225 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {8226 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
8226 .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),8227 .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),
8228 .error_set => value.getErrorInt(zcu),8229 .error_set => value.getErrorInt(zcu),
8229 .pointer => value.toUnsignedInt(zcu),8230 .pointer => value.toUnsignedInt(zcu),
8230 else => unreachable,8231 else => unreachable,
...@@ -8378,7 +8379,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {...@@ -8378,7 +8379,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
8378 break :blk if (backing_bits <= 32) 1 else 2;8379 break :blk if (backing_bits <= 32) 1 else 2;
8379 },8380 },
8380 .@"enum" => blk: {8381 .@"enum" => blk: {
8381 const int_ty = cond_ty.intTagType(zcu);8382 const int_ty = cond_ty.backingIntType(zcu);
8382 const int_info = int_ty.intInfo(zcu);8383 const int_info = int_ty.intInfo(zcu);
8383 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);8384 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);
8384 if (big_int) return cg.todo("implement composite int loop switch", .{});8385 if (big_int) return cg.todo("implement composite int loop switch", .{});
...@@ -8464,7 +8465,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {...@@ -8464,7 +8465,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
8464 const value: Value = .fromInterned(item.toInterned().?);8465 const value: Value = .fromInterned(item.toInterned().?);
8465 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {8466 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
8466 .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),8467 .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),
8468 .error_set => value.getErrorInt(zcu),8469 .error_set => value.getErrorInt(zcu),
8469 .pointer => value.toUnsignedInt(zcu),8470 .pointer => value.toUnsignedInt(zcu),
8470 else => unreachable,8471 else => unreachable,
src/codegen/wasm/CodeGen.zig+9-15
...@@ -32,6 +32,7 @@ const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;...@@ -32,6 +32,7 @@ const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
3232
33pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {33pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
34 return comptime &.initMany(&.{34 return comptime &.initMany(&.{
35 .expand_bit_cast_safe,
35 .expand_int_cast_safe,36 .expand_int_cast_safe,
36 .expand_int_from_float_safe,37 .expand_int_from_float_safe,
37 .expand_int_from_float_optimized_safe,38 .expand_int_from_float_optimized_safe,
...@@ -615,7 +616,7 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w...@@ -615,7 +616,7 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w
615 .unrolled => .i32,616 .unrolled => .i32,
616 },617 },
617 .@"union", .@"struct" => switch (ty.containerLayout(zcu)) {618 .@"union", .@"struct" => switch (ty.containerLayout(zcu)) {
618 .@"packed" => typeToValtype(ty.bitpackBackingInt(zcu), zcu, target),619 .@"packed" => typeToValtype(ty.backingIntType(zcu), zcu, target),
619 .auto, .@"extern" => .i32,620 .auto, .@"extern" => .i32,
620 },621 },
621 else => .i32, // all represented as reference/immediate622 else => .i32, // all represented as reference/immediate
...@@ -1226,7 +1227,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {...@@ -1226,7 +1227,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {
1226 .frame,1227 .frame,
1227 => return ty.hasRuntimeBits(zcu),1228 => return ty.hasRuntimeBits(zcu),
1228 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {1229 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
1229 .@"packed" => return isByRef(ty.bitpackBackingInt(zcu), zcu, target),1230 .@"packed" => return isByRef(ty.backingIntType(zcu), zcu, target),
1230 .@"extern", .auto => return ty.hasRuntimeBits(zcu),1231 .@"extern", .auto => return ty.hasRuntimeBits(zcu),
1231 },1232 },
1232 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,1233 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,
...@@ -1905,6 +1906,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1905,6 +1906,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1905 .add_safe,1906 .add_safe,
1906 .sub_safe,1907 .sub_safe,
1907 .mul_safe,1908 .mul_safe,
1909 .bit_cast_safe,
1908 .int_cast_safe,1910 .int_cast_safe,
1909 .int_from_float_safe,1911 .int_from_float_safe,
1910 .int_from_float_optimized_safe,1912 .int_from_float_optimized_safe,
...@@ -2525,18 +2527,10 @@ const IntType = struct {...@@ -2525,18 +2527,10 @@ const IntType = struct {
2525 .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable,2527 .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable,
2526 .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable,2528 .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable,
2527 },2529 },
2528 .struct_type => {2530 .enum_type,
2529 const loaded_struct = ip.loadStructType(ty_index);2531 .struct_type,
2530 switch (loaded_struct.layout) {2532 .union_type,
2531 .auto, .@"extern" => unreachable,2533 => ty_index = Type.fromInterned(ty_index).backingIntType(zcu).toIntern(),
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,
2540 .error_set_type, .inferred_error_set_type => return .{ .is_signed = false, .bits = zcu.errorSetBits() },2534 .error_set_type, .inferred_error_set_type => return .{ .is_signed = false, .bits = zcu.errorSetBits() },
2541 else => unreachable,2535 else => unreachable,
2542 };2536 };
...@@ -5134,7 +5128,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {...@@ -5134,7 +5128,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
5134 return .{ .imm32 = 0xaaaaaaaa };5128 return .{ .imm32 = 0xaaaaaaaa };
5135 },5129 },
5136 .@"struct", .@"union" => {5130 .@"struct", .@"union" => {
5137 const backing_int_ty = ty.bitpackBackingInt(zcu);5131 const backing_int_ty = ty.backingIntType(zcu);
5138 return cg.emitUndefined(backing_int_ty);5132 return cg.emitUndefined(backing_int_ty);
5139 },5133 },
5140 else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}),5134 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 {...@@ -63,6 +63,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
63 .reduce_one_elem_to_bit_cast,63 .reduce_one_elem_to_bit_cast,
64 .splat_one_elem_to_bit_cast,64 .splat_one_elem_to_bit_cast,
6565
66 .expand_bit_cast_safe,
66 .expand_int_cast_safe,67 .expand_int_cast_safe,
67 .expand_int_from_float_safe,68 .expand_int_from_float_safe,
68 .expand_int_from_float_optimized_safe,69 .expand_int_from_float_optimized_safe,
...@@ -67446,6 +67447,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -67446,6 +67447,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
67446 .int_from_error,67447 .int_from_error,
67447 .union_from_enum,67448 .union_from_enum,
67448 => try cg.airBitCast(inst),67449 => try cg.airBitCast(inst),
67450 .bit_cast_safe => unreachable,
67449 .block => {67451 .block => {
67450 const block = cg.air.unwrapBlock(inst);67452 const block = cg.air.unwrapBlock(inst);
67451 if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none);67453 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 {...@@ -5098,6 +5098,7 @@ fn DeclValEnum(comptime T: type) type {
5098 if (min_value == null or min_value.? > value) min_value = value;5098 if (min_value == null or min_value.? > value) min_value = value;
5099 if (max_value == null or max_value.? < value) max_value = value;5099 if (max_value == null or max_value.? < value) max_value = value;
5100 }5100 }
5101 if (fields_len == 0) return enum {};
5101 const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0);5102 const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0);
5102 var field_vals: [fields_len]TagInt = undefined;5103 var field_vals: [fields_len]TagInt = undefined;
5103 for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name);5104 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 {...@@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
151 assert(ip.indexToKey(data.ip_index) == .enum_type);151 assert(ip.indexToKey(data.ip_index) == .enum_type);
152 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);152 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
153 if (!gop.found_existing) {153 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);
155 gop.value_ptr.* = .{ .tag_name = .{155 gop.value_ptr.* = .{ .tag_name = .{
156 .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}),156 .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}),
157 .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target),157 .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(...@@ -94,15 +94,14 @@ pub fn print(
94 enum_literal.fmt(ip),94 enum_literal.fmt(ip),
95 }),95 }),
96 .enum_tag => |enum_tag| {96 .enum_tag => |enum_tag| {
97 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());97 const ty: Type = .fromInterned(enum_tag.ty);
98 if (enum_type.tagValueIndex(ip, enum_tag.int)) |tag_index| {98 const enum_obj = ip.loadEnumType(ty.toIntern());
99 return writer.print(".{f}", .{enum_type.field_names.get(ip)[tag_index].fmt(ip)});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)});
100 }101 }
101 if (level == 0) {102 try writer.writeAll("@fromBackingInt(");
102 return writer.writeAll("@enumFromInt(...)");103 if (level == 0) return writer.writeAll("...)");
103 }104 try print(.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
104 try writer.writeAll("@enumFromInt(");
105 try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
106 try writer.writeAll(")");105 try writer.writeAll(")");
107 },106 },
108 .float => |float| switch (float.storage) {107 .float => |float| switch (float.storage) {
...@@ -190,10 +189,17 @@ pub fn print(...@@ -190,10 +189,17 @@ pub fn print(
190 try writer.writeAll(" }");189 try writer.writeAll(" }");
191 return;190 return;
192 },191 },
193 .@"union" => {192 .@"union" => switch (ty.backingIntMode(zcu)) {
194 try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)});193 .auto => {
195 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);194 try writer.print("@bitCast(@as({f}, ", .{ty.backingIntType(zcu).fmt(pt)});
196 try writer.writeAll("))");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 },
197 },203 },
198 else => unreachable,204 else => unreachable,
199 }205 }
src/print_zir.zig+14
...@@ -193,6 +193,7 @@ const Writer = struct {...@@ -193,6 +193,7 @@ const Writer = struct {
193 .elem_type,193 .elem_type,
194 .indexable_ptr_elem_type,194 .indexable_ptr_elem_type,
195 .splat_op_result_ty,195 .splat_op_result_ty,
196 .from_backing_int_arg_ty,
196 .indexable_ptr_len,197 .indexable_ptr_len,
197 .anyframe_type,198 .anyframe_type,
198 .bit_not,199 .bit_not,
...@@ -232,6 +233,7 @@ const Writer = struct {...@@ -232,6 +233,7 @@ const Writer = struct {
232 .compile_error,233 .compile_error,
233 .set_eval_branch_quota,234 .set_eval_branch_quota,
234 .int_from_enum,235 .int_from_enum,
236 .backing_int,
235 .align_of,237 .align_of,
236 .int_from_bool,238 .int_from_bool,
237 .embed_file,239 .embed_file,
...@@ -418,6 +420,8 @@ const Writer = struct {...@@ -418,6 +420,8 @@ const Writer = struct {
418420
419 .for_len => try self.writePlNodeMultiOp(stream, inst),421 .for_len => try self.writePlNodeMultiOp(stream, inst),
420422
423 .from_backing_int => try self.writePlNodeBin(stream, inst),
424
421 .elem_val_imm => try self.writeElemValImm(stream, inst),425 .elem_val_imm => try self.writeElemValImm(stream, inst),
422426
423 .@"export" => try self.writePlNodeExport(stream, inst),427 .@"export" => try self.writePlNodeExport(stream, inst),
...@@ -985,6 +989,16 @@ const Writer = struct {...@@ -985,6 +989,16 @@ const Writer = struct {
985 try self.writeSrcNode(stream, inst_data.src_node);989 try self.writeSrcNode(stream, inst_data.src_node);
986 }990 }
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
988 fn writeBuiltinCall(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {1002 fn writeBuiltinCall(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
989 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;1003 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
990 const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;1004 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 {...@@ -6,6 +6,7 @@ test {
6 _ = @import("behavior/alignof.zig");6 _ = @import("behavior/alignof.zig");
7 _ = @import("behavior/array.zig");7 _ = @import("behavior/array.zig");
8 _ = @import("behavior/atomics.zig");8 _ = @import("behavior/atomics.zig");
9 _ = @import("behavior/backing_int.zig");
9 _ = @import("behavior/basic.zig");10 _ = @import("behavior/basic.zig");
10 _ = @import("behavior/bit_shifting.zig");11 _ = @import("behavior/bit_shifting.zig");
11 _ = @import("behavior/bitcast.zig");12 _ = @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" {...@@ -1451,29 +1451,6 @@ test "comptime @enumFromInt with signed arithmetic" {
1451 comptime assert(@intFromEnum(x) == 0);1451 comptime assert(@intFromEnum(x) == 0);
1452}1452}
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
1477test "enum int tag type uses declaration inside the enum" {1454test "enum int tag type uses declaration inside the enum" {
1478 const static = struct {1455 const static = struct {
1479 const E = enum(E.IntTag) {1456 const E = enum(E.IntTag) {
...@@ -1488,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" {...@@ -1488,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" {
1488 try expect(val == .b);1465 try expect(val == .b);
1489 try expect(@intFromEnum(val) == 1);1466 try expect(@intFromEnum(val) == 1);
1490}1467}
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" {...@@ -122,7 +122,7 @@ test "correct sizeOf and offsets in packed structs" {
122 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO122 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
123 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO123 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
124124
125 const PStruct = packed struct {125 const PStruct = packed struct(u32) {
126 bool_a: bool,126 bool_a: bool,
127 bool_b: bool,127 bool_b: bool,
128 bool_c: bool,128 bool_c: bool,
...@@ -162,7 +162,7 @@ test "correct sizeOf and offsets in packed structs" {...@@ -162,7 +162,7 @@ test "correct sizeOf and offsets in packed structs" {
162 try expectEqual(22, @bitOffsetOf(PStruct, "u10_b"));162 try expectEqual(22, @bitOffsetOf(PStruct, "u10_b"));
163 try expectEqual(4, @sizeOf(PStruct));163 try expectEqual(4, @sizeOf(PStruct));
164164
165 const s1 = @as(PStruct, @bitCast(@as(u32, 0x12345678)));165 const s1: PStruct = @fromBackingInt(0x12345678);
166 try expectEqual(false, s1.bool_a);166 try expectEqual(false, s1.bool_a);
167 try expectEqual(false, s1.bool_b);167 try expectEqual(false, s1.bool_b);
168 try expectEqual(false, s1.bool_c);168 try expectEqual(false, s1.bool_c);
...@@ -176,7 +176,7 @@ test "correct sizeOf and offsets in packed structs" {...@@ -176,7 +176,7 @@ test "correct sizeOf and offsets in packed structs" {
176 try expectEqual(0b1101000101, s1.u10_a);176 try expectEqual(0b1101000101, s1.u10_a);
177 try expectEqual(0b0001001000, s1.u10_b);177 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);
180 try expectEqual(0, s2.x);180 try expectEqual(0, s2.x);
181 try expectEqual(0b1111010, s2.y);181 try expectEqual(0b1111010, s2.y);
182 try expectEqual(0xd5c71f, s2.z);182 try expectEqual(0xd5c71f, s2.z);
...@@ -191,7 +191,7 @@ test "nested packed structs" {...@@ -191,7 +191,7 @@ test "nested packed structs" {
191 const S2 = packed struct { d: u8, e: u8, f: u8 };191 const S2 = packed struct { d: u8, e: u8, f: u8 };
192192
193 const S3 = packed struct { x: S1, y: S2 };193 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
196 try expectEqual(48, @bitSizeOf(S3));196 try expectEqual(48, @bitSizeOf(S3));
197 try expectEqual(@sizeOf(u48), @sizeOf(S3));197 try expectEqual(@sizeOf(u48), @sizeOf(S3));
...@@ -199,7 +199,7 @@ test "nested packed structs" {...@@ -199,7 +199,7 @@ test "nested packed structs" {
199 try expectEqual(3, @offsetOf(S3, "y"));199 try expectEqual(3, @offsetOf(S3, "y"));
200 try expectEqual(24, @bitOffsetOf(S3, "y"));200 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;
203 try expectEqual(0xf4, s3.x.a);203 try expectEqual(0xf4, s3.x.a);
204 try expectEqual(0x1f, s3.x.b);204 try expectEqual(0x1f, s3.x.b);
205 try expectEqual(0xc7, s3.x.c);205 try expectEqual(0xc7, s3.x.c);
...@@ -558,7 +558,7 @@ test "packed struct fields modification" {...@@ -558,7 +558,7 @@ test "packed struct fields modification" {
558 // Originally reported at https://github.com/ziglang/zig/issues/16615558 // Originally reported at https://github.com/ziglang/zig/issues/16615
559 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;559 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
560560
561 const Small = packed struct {561 const Small = packed struct(u16) {
562 val: u8 = 0,562 val: u8 = 0,
563 lo: u4 = 0,563 lo: u4 = 0,
564 hi: u4 = 0,564 hi: u4 = 0,
...@@ -570,12 +570,12 @@ test "packed struct fields modification" {...@@ -570,12 +570,12 @@ test "packed struct fields modification" {
570 .lo = 3,570 .lo = 3,
571 .hi = 4,571 .hi = 4,
572 };572 };
573 try expect(@as(u16, @bitCast(Small.p)) == 0x4312);573 try expect(@backingInt(Small.p) == 0x4312);
574574
575 Small.p.val -= Small.p.lo;575 Small.p.val -= Small.p.lo;
576 Small.p.val += Small.p.hi;576 Small.p.val += Small.p.hi;
577 Small.p.hi -= Small.p.lo;577 Small.p.hi -= Small.p.lo;
578 try expect(@as(u16, @bitCast(Small.p)) == 0x1313);578 try expect(@backingInt(Small.p) == 0x1313);
579}579}
580580
581test "nested packed struct field access test" {581test "nested packed struct field access test" {
...@@ -1246,3 +1246,31 @@ test "initialize packed struct field to undefined at comptime" {...@@ -1246,3 +1246,31 @@ test "initialize packed struct field to undefined at comptime" {
1246 const val: S = .{ .x = undefined };1246 const val: S = .{ .x = undefined };
1247 _ = val;1247 _ = val;
1248}1248}
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" {...@@ -227,3 +227,30 @@ test "initialize packed union field to undefined at comptime" {
227 const val: U = .{ .x = undefined };227 const val: U = .{ .x = undefined };
228 _ = val;228 _ = val;
229}229}
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" {...@@ -1132,13 +1132,13 @@ test "decl literals as switch cases" {
1132 try comptime E.doTheTest(.foo);1132 try comptime E.doTheTest(.foo);
1133}1133}
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.
1136// When we do that, consider adding an 'error{}' case if possible.1136// When we do that, consider adding an 'error{}' case if possible.
1137test "switch with uninstantiable union fields" {1137test "switch with uninstantiable union fields" {
1138 const U = union(enum) {1138 const U = union(enum) {
1139 ok: void,1139 ok: void,
1140 a: noreturn,1140 a: noreturn,
1141 b: noreturn,1141 b: enum {},
11421142
1143 fn doTheTest(u: @This()) void {1143 fn doTheTest(u: @This()) void {
1144 switch (u) {1144 switch (u) {
test/behavior/type.zig+11-3
...@@ -266,13 +266,21 @@ test "Type.Union from regular enum" {...@@ -266,13 +266,21 @@ test "Type.Union from regular enum" {
266test "Type.Union from empty regular enum" {266test "Type.Union from empty regular enum" {
267 const E = enum {};267 const E = enum {};
268 const U = @Union(.auto, E, &.{}, &.{}, &.{});268 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);
270}274}
271275
272test "Type.Union from empty Type.Enum" {276test "Type.Union from empty Type.Enum" {
273 const E = @Enum(u0, .exhaustive, &.{}, &.{});277 const E = @Enum(noreturn, .exhaustive, &.{}, &.{});
274 const U = @Union(.auto, E, &.{}, &.{}, &.{});278 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);
276}284}
277285
278test "Type.Fn" {286test "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 @@...@@ -1,15 +1,23 @@
1pub const E = enum(u31) { A, B, C };1export fn entry1() void {
2pub const S = extern struct {2 const E = enum(u31) { A, B, C };
3 e: E,3 _ = @sizeOf(extern struct {
4};4 x: E,
5export fn entry() void {5 });
6 const s: S = undefined;6}
7 _ = s;7export fn entry2() void {
8 const E = enum(noreturn) {};
9 _ = @sizeOf(extern struct {
10 x: E,
11 });
8}12}
913
10// error14// error
11//15//
12// :3:8: error: extern structs cannot contain fields of type 'tmp.E'16// :4:12: error: extern structs cannot contain fields of type 'tmp.entry1.E'
13// :1:15: note: enum tag type 'u31' is not extern compatible17// :2: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 compatible18// :2:15: note: only integers with 0 or power of two bits are extern compatible
15// :1:15: note: enum declared here19// :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 @@...@@ -1,10 +1,10 @@
1const EnumInferred = enum {};1const EnumInferred = enum {};
2const EnumExplicit = enum(u8) {};2const EnumExplicit = enum(noreturn) {};
3const EnumNonexhaustive = enum(u8) { _ };3const EnumNonexhaustive = enum(u8) { _ };
44
5const U0 = union {};5const U0 = union {};
6const U1 = union(enum) {};6const U1 = union(enum) {};
7const U2 = union(enum(u8)) {};7const U2 = union(enum(noreturn)) {};
8const U3 = union(EnumInferred) {};8const U3 = union(EnumInferred) {};
9const U4 = union(EnumExplicit) {};9const U4 = union(EnumExplicit) {};
10const U5 = union(EnumNonexhaustive) {};10const 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 {...@@ -22,5 +22,5 @@ export fn bar() void {
22//22//
23// :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum23// :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum
24// :1:11: note: enum declared here24// :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)'
26// :6:11: note: union declared here26// :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 {...@@ -81,6 +81,12 @@ export fn entry15() void {
81 x: *const u32,81 x: *const u32,
82 });82 });
83}83}
84export fn entry16() void {
85 const E = enum(noreturn) {};
86 _ = @sizeOf(packed struct {
87 x: E,
88 });
89}
8490
85// error91// error
86//92//
...@@ -114,3 +120,5 @@ export fn entry15() void {...@@ -114,3 +120,5 @@ export fn entry15() void {
114// :81:12: error: packed structs cannot contain fields of type '*const u32'120// :81:12: error: packed structs cannot contain fields of type '*const u32'
115// :81:12: note: pointers cannot be directly bitpacked121// :81:12: note: pointers cannot be directly bitpacked
116// :81:12: note: consider using 'usize' and '@intFromPtr'122// :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 {...@@ -10,6 +10,12 @@ export fn entry1() void {
10 x: *const u32,10 x: *const u32,
11 });11 });
12}12}
13export fn entry2() void {
14 const E = enum(noreturn) {};
15 _ = @sizeOf(packed union {
16 x: E,
17 });
18}
1319
14// error20// error
15//21//
...@@ -19,3 +25,5 @@ export fn entry1() void {...@@ -19,3 +25,5 @@ export fn entry1() void {
19// :10:12: error: packed unions cannot contain fields of type '*const u32'25// :10:12: error: packed unions cannot contain fields of type '*const u32'
20// :10:12: note: pointers cannot be directly bitpacked26// :10:12: note: pointers cannot be directly bitpacked
21// :10:12: note: consider using 'usize' and '@intFromPtr'27// :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 @@...@@ -1,4 +1,4 @@
1const Tag = @Enum(u0, .exhaustive, &.{}, &.{});1const Tag = @Enum(noreturn, .exhaustive, &.{}, &.{});
2const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{}));2const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{}));
3export fn entry() void {3export fn entry() void {
4 const tagged: Tagged = undefined;4 const tagged: Tagged = undefined;
test/cases/compile_errors/sizeof_alignof_empty_union.zig+2-2
...@@ -1,10 +1,10 @@...@@ -1,10 +1,10 @@
1const EnumInferred = enum {};1const EnumInferred = enum {};
2const EnumExplicit = enum(u8) {};2const EnumExplicit = enum(noreturn) {};
3const EnumNonexhaustive = enum(u8) { _ };3const EnumNonexhaustive = enum(u8) { _ };
44
5const U0 = union {};5const U0 = union {};
6const U1 = union(enum) {};6const U1 = union(enum) {};
7const U2 = union(enum(u8)) {};7const U2 = union(enum(noreturn)) {};
8const U3 = union(EnumInferred) {};8const U3 = union(EnumInferred) {};
9const U4 = union(EnumExplicit) {};9const U4 = union(EnumExplicit) {};
10const U5 = union(EnumNonexhaustive) {};10const U5 = union(EnumNonexhaustive) {};
test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig+1-1
...@@ -6,5 +6,5 @@ test "enum" {...@@ -6,5 +6,5 @@ test "enum" {
6// error6// error
7// is_test=true7// is_test=true
8//8//
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'
10// :2:15: note: declared here10// :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