authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-06-03 15:27:46+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-17 09:43:53+01:00
logcb4c344e19a269ac227489a96e2ef53dd077ece0
treef5300c74c783b25fd1e0398e9810f171d5767f87
parent9aeac329a25ddf43bf3afdfe8c381682226dafdf
signaturelock-open Commit is signed but in an unrecognized format.

compiler: add `@backingInt` and `@fromBackingInt`

This commit implements the `@backingInt` and `@fromBackingInt` builtins. These builtins are meant to replace the now-deprecated `@intFromEnum` and `@enumFromInt` builtins. `@backingInt` works with all enums and with bitpacks with *explicit* backing integer types only. It also works with tagged unions, returning the backing integer of the active tag value. An `undefined` enum or bitpack yields an `undefined` backing integer. The builtin is lowered to `bit_cast`. `@fromBackingInt` infers its result type, which may be any enum or a bitpack with an *explicit* backing integer type. It takes a parameter of exactly that backing integer type. For enums, passing a backing integer that is either `undefined` or would yield an invalid tag value results in safety-checked Illegal Behavior. For bitpacks, passing an `undefined` backing integer yields an `undefined` bitpack. The builtin is lowered to `bit_cast` or `bit_cast_safe` (more details below). `@bitCast` now also performs a safety check for invalid tag values if its destination type is an `enum`. This commit also introduces a new AIR instruction, `bit_cast_safe`. Similarly to `int_cast_safe`, it includes a safety check for invalid enum tag values. When `@enumFromInt` is eventually removed, `int_cast_safe` won't need to perform this safety check anymore. Legalize only implements `expand_bit_cast_safe`, and no additional insts to scalarize `bit_cast_safe` with vector/array operands. That is because it can just reuse the existing scalarizations for regular `bit_cast`. The safety check mandated by `bit_cast_safe` is only desired if the result type is a scalar enum tag value, so the scalarization process is exactly equivalent. The only difference is that we have to perform a safety check on the result of the scalarization. This means that there's no reason for a backend to want scalarization of regular `bit_cast`, but not of `bit_cast_safe` (or the other way round). If a backend is having trouble with the `bit_cast_safe` safety check it should just use `expand_bit_cast_safe` instead. Also adds a `std.meta.BackingInt()` function to conveniently get the result type of `@backingInt` and for use in the langref.

60 files changed, 1578 insertions(+), 255 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|noreturn#}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+11-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.
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+31-1
...@@ -506,10 +506,40 @@ pub fn BareUnion(comptime T: type) type {...@@ -506,10 +506,40 @@ pub fn BareUnion(comptime T: type) type {
506 .@"union" => |u| u,506 .@"union" => |u| u,
507 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),507 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),
508 };508 };
509
510 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..]);
511}510}
512511
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
513pub fn Tag(comptime T: type) type {543pub fn Tag(comptime T: type) type {
514 return switch (@typeInfo(T)) {544 return switch (@typeInfo(T)) {
515 .@"enum" => |info| info.tag_type,545 .@"enum" => |info| info.tag_type,
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,
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+110-4
...@@ -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,6 +604,17 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -600,6 +604,17 @@ 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 continue :inst l.replaceInst(inst, .block, try l.safeBitcastBlockPayload(inst));
609 } else if (l.features.hasAny(&.{
610 .scalarize_bit_cast_array,
611 .scalarize_bit_cast_vector_non_elementwise,
612 .scalarize_bit_cast_padded_elems,
613 })) {
614 if (try l.scalarizeBitcastBlockPayload(inst)) |payload| {
615 continue :inst l.replaceInst(inst, .block, payload);
616 }
617 },
603 .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) {618 .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 both619 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));620 continue :inst l.replaceInst(inst, .block, try l.safeIntcastBlockPayload(inst));
...@@ -1006,7 +1021,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz...@@ -1006,7 +1021,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
10061021
1007 if (result_is_array) {1022 if (result_is_array) {
1008 // This is only allowed when legalizing an elementwise bitcast.1023 // This is only allowed when legalizing an elementwise bitcast.
1009 assert(orig.tag == .bit_cast);1024 switch (orig.tag) {
1025 .bit_cast, .bit_cast_safe => {},
1026 else => unreachable,
1027 }
1010 assert(form == .ty_op);1028 assert(form == .ty_op);
1011 }1029 }
10121030
...@@ -1076,7 +1094,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz...@@ -1076,7 +1094,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
1076 orig_operand,1094 orig_operand,
1077 index_val,1095 index_val,
1078 ).toRef();1096 ).toRef();
1079 break :elem loop.block.addTyOp(l, orig.tag, res_elem_ty, operand).toRef();1097 const scalar_tag: Air.Inst.Tag = switch (orig.tag) {
1098 .bit_cast_safe => .bit_cast, // safety check is not supposed to be elementwise
1099 else => orig.tag,
1100 };
1101 break :elem loop.block.addTyOp(l, scalar_tag, res_elem_ty, operand).toRef();
1080 },1102 },
1081 .bin_op => elem: {1103 .bin_op => elem: {
1082 const orig_bin = orig.data.bin_op;1104 const orig_bin = orig.data.bin_op;
...@@ -1700,9 +1722,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?...@@ -1700,9 +1722,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?
17001722
1701 // Now convert `uint_ty` (`uN`) to `dest_ty`.1723 // Now convert `uint_ty` (`uN`) to `dest_ty`.
17021724
1725 // We omit the safety check when casting to an array or a vector since it's
1726 // not supposed to be elementwise.
1727 if (dest_ty.zigTypeTag(zcu) == .@"enum") assert(int_to_dest_ok);
1728
1703 if (int_to_dest_ok) {1729 if (int_to_dest_ok) {
1704 _ = main_block.stealCapacity(17);1730 _ = main_block.stealCapacity(17);
1705 const result = main_block.addBitCast(l, dest_ty, uint_val);1731 const result = switch (l.air_instructions.items(.tag)[@intFromEnum(orig_inst)]) {
1732 .bit_cast => main_block.addBitCast(l, dest_ty, uint_val),
1733 .bit_cast_safe => main_block.add(l, .{
1734 .tag = .bit_cast_safe,
1735 .data = .{ .ty_op = .{
1736 .ty = .fromType(dest_ty),
1737 .operand = uint_val,
1738 } },
1739 }).toRef(),
1740 else => unreachable,
1741 };
1706 main_block.addBr(l, orig_inst, result);1742 main_block.addBr(l, orig_inst, result);
1707 } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) {1743 } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) {
1708 _ = main_block.stealCapacity(16);1744 _ = main_block.stealCapacity(16);
...@@ -2089,6 +2125,76 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize...@@ -2089,6 +2125,76 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize
2089 } };2125 } };
2090}2126}
20912127
2128fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2129 const pt = l.pt;
2130 const zcu = pt.zcu;
2131 const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op;
2132
2133 const operand_ref = ty_op.operand;
2134 const dest_ty = ty_op.ty.toType();
2135
2136 // The worst case is a bitcast to an exhaustive enum and looks like this:
2137 //
2138 // %x = block({
2139 // %1 = bit_cast(@res_ty, %y)
2140 // %2 = is_named_enum_value(%1)
2141 // %3 = cond_br(%2, {
2142 // %4 = br(%x, %1)
2143 // }, {
2144 // %5 = call(@panic.invalidEnumValue, [])
2145 // %6 = unreach()
2146 // })
2147 // })
2148
2149 var inst_buf: [6]Air.Inst.Index = undefined;
2150 try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len);
2151 var opt_condbr: ?CondBr = null;
2152
2153 var main_block: Block = .init(&inst_buf);
2154 var cur_block: *Block = &main_block;
2155
2156 const cast_inst = cur_block.addBitCast(l, dest_ty, operand_ref);
2157
2158 if (dest_ty.zigTypeTag(zcu) == .@"enum" and
2159 !dest_ty.isNonexhaustiveEnum(zcu) and
2160 zcu.backendSupportsFeature(.is_named_enum_value))
2161 {
2162 // We are building this:
2163 // %1 = is_named_enum_value(%cast_inst)
2164 // %2 = cond_br(%1, {
2165 // <new cursor>
2166 // }, {
2167 // <panic>
2168 // })
2169 const is_named_inst = cur_block.add(l, .{
2170 .tag = .is_named_enum_value,
2171 .data = .{ .un_op = cast_inst },
2172 });
2173 opt_condbr = .init(l, is_named_inst.toRef(), cur_block, .{ .false = .cold });
2174 const condbr = &(opt_condbr.?);
2175 condbr.else_block = .init(cur_block.stealRemainingCapacity());
2176 try condbr.else_block.addPanic(l, .invalid_enum_value);
2177 condbr.then_block = .init(condbr.else_block.stealRemainingCapacity());
2178 cur_block = &condbr.then_block;
2179 }
2180 // Finally, just `br` to our outer `block`.
2181 _ = cur_block.add(l, .{
2182 .tag = .br,
2183 .data = .{ .br = .{
2184 .block_inst = orig_inst,
2185 .operand = cast_inst,
2186 } },
2187 });
2188 // We might not have used all of the instructions; that's intentional.
2189 _ = cur_block.stealRemainingCapacity();
2190
2191 if (opt_condbr) |condbr| try condbr.finish(l);
2192 return .{ .ty_pl = .{
2193 .ty = .fromType(dest_ty),
2194 .payload = try l.addBlockBody(main_block.body()),
2195 } };
2196}
2197
2092fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {2198fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2093 const pt = l.pt;2199 const pt = l.pt;
2094 const zcu = pt.zcu;2200 const zcu = pt.zcu;
...@@ -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: {
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+1
...@@ -10192,6 +10192,7 @@ pub fn getCoerced(...@@ -10192,6 +10192,7 @@ pub fn getCoerced(
10192 .enum_type => {10192 .enum_type => {
10193 const enum_type = ip.loadEnumType(new_ty);10193 const enum_type = ip.loadEnumType(new_ty);
10194 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);
10195 return ip.get(gpa, io, tid, .{ .enum_tag = .{10196 return ip.get(gpa, io, tid, .{ .enum_tag = .{
10196 .ty = new_ty,10197 .ty = new_ty,
10197 .int = if (enum_type.field_values.len != 0)10198 .int = if (enum_type.field_values.len != 0)
src/Sema.zig+247-73
...@@ -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,12 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -7853,12 +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);
7857 assert(int_tag_ty.classify(zcu) != .no_possible_value);7882 assert(int_tag_ty.classify(zcu) != .no_possible_value);
78587883
7859 if (sema.resolveValue(enum_tag)) |enum_tag_val| {7884 if (sema.resolveValue(enum_tag)) |enum_tag_val| {
7860 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);
7861 return .fromValue(enum_tag_val.intFromEnum(zcu));7886 return .fromValue(enum_tag_val.backingInt(zcu));
7862 }7887 }
78637888
7864 try sema.requireRuntimeBlock(block, src, operand_src);7889 try sema.requireRuntimeBlock(block, src, operand_src);
...@@ -7884,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -7884,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
78847909
7885 if (sema.resolveValue(operand)) |int_val| {7910 if (sema.resolveValue(operand)) |int_val| {
7886 if (dest_ty.isNonexhaustiveEnum(zcu)) {7911 if (dest_ty.isNonexhaustiveEnum(zcu)) {
7887 const int_tag_ty = dest_ty.intTagType(zcu);7912 const int_tag_ty = dest_ty.backingIntType(zcu);
7888 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {7913 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {
7889 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());7914 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
7890 }7915 }
...@@ -7907,7 +7932,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -7907,7 +7932,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
7907 if (block.wantSafety()) {7932 if (block.wantSafety()) {
7908 // The operand is runtime-known but the result is comptime-known. In7933 // The operand is runtime-known but the result is comptime-known. In
7909 // this case we still need a safety check.7934 // this case we still need a safety check.
7910 const expect_int = try pt.getCoerced(opv.intFromEnum(zcu), operand_ty);7935 const expect_int = try pt.getCoerced(opv.backingInt(zcu), operand_ty);
7911 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));7936 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));
7912 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);7937 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
7913 }7938 }
...@@ -9361,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9361,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9361 .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)}),
9362 else => {},9387 else => {},
9363 }9388 }
9364
9365 break :msg msg;9389 break :msg msg;
9366 }),9390 }),
9367 .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {9391 .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {
...@@ -9396,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9396,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9396 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)});
9397 }9421 }
93989422
9399 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);
9400}9600}
94019601
9402fn 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 {
...@@ -9929,6 +10129,9 @@ fn analyzeSwitchBlock(...@@ -9929,6 +10129,9 @@ fn analyzeSwitchBlock(
9929 operand_ty.containerLayout(zcu) != .@"packed";10129 operand_ty.containerLayout(zcu) != .@"packed";
9930 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;10130 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
993110131
10132 // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909)
10133 if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated
10134
9932 const cond_ref = switch (operand) {10135 const cond_ref = switch (operand) {
9933 .simple => |s| s.cond,10136 .simple => |s| s.cond,
9934 .loop => |l| l.init_cond,10137 .loop => |l| l.init_cond,
...@@ -10433,26 +10636,11 @@ fn finishSwitchBr(...@@ -10433,26 +10636,11 @@ fn finishSwitchBr(
10433 }10636 }
1043410637
10435 var prev_result_overflowed = false;10638 var prev_result_overflowed = false;
10436 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({10639 while (item.compareScalar(.lte, item_last, item_ty, zcu)) : ({
10437 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
10438 .int => .{ item, operand_ty },
10439 .@"enum" => b: {
10440 const int_val: Value = .fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
10441 break :b .{ int_val, int_val.typeOf(zcu) };
10442 },
10443 else => unreachable,
10444 };
10445 assert(!prev_result_overflowed);10640 assert(!prev_result_overflowed);
10446 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);10641 const result = try arith.incrementDefinedInt(sema, item_ty, item);
10447 prev_result_overflowed = result.overflow;10642 prev_result_overflowed = result.overflow;
10448 item = switch (operand_ty.zigTypeTag(zcu)) {10643 item = result.val;
10449 .int => result.val,
10450 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
10451 .ty = operand_ty.toIntern(),
10452 .int = result.val.toIntern(),
10453 } })),
10454 else => unreachable,
10455 };
10456 }) {10644 }) {
10457 cases_len += 1;10645 cases_len += 1;
10458 case_block.instructions.clearRetainingCapacity();10646 case_block.instructions.clearRetainingCapacity();
...@@ -10585,7 +10773,7 @@ fn finishSwitchBr(...@@ -10585,7 +10773,7 @@ fn finishSwitchBr(
10585 break :check_enumerable .{ undefined, min_int };10773 break :check_enumerable .{ undefined, min_int };
10586 },10774 },
10587 .@"union", .@"struct" => {10775 .@"union", .@"struct" => {
10588 const backing_int_ty = item_ty.bitpackBackingInt(zcu);10776 const backing_int_ty = item_ty.backingIntType(zcu);
10589 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);
10590 break :check_enumerable .{ undefined, min_backing_int };10778 break :check_enumerable .{ undefined, min_backing_int };
10591 },10779 },
...@@ -10886,7 +11074,7 @@ const ValidatedSwitchBlock = struct {...@@ -10886,7 +11074,7 @@ const ValidatedSwitchBlock = struct {
10886 var cur_val = it.next_val orelse return null;11074 var cur_val = it.next_val orelse return null;
10887 const int_ty = switch (type_tag) {11075 const int_ty = switch (type_tag) {
10888 .int => item_ty,11076 .int => item_ty,
10889 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),11077 .@"union", .@"struct" => item_ty.backingIntType(zcu),
10890 else => unreachable,11078 else => unreachable,
10891 };11079 };
10892 while (it.next_idx < it.seen_ranges.len and11080 while (it.next_idx < it.seen_ranges.len and
...@@ -11316,7 +11504,7 @@ fn validateSwitchBlock(...@@ -11316,7 +11504,7 @@ fn validateSwitchBlock(
11316 check_range: {11504 check_range: {
11317 const int_ty = switch (type_tag) {11505 const int_ty = switch (type_tag) {
11318 .int => item_ty,11506 .int => item_ty,
11319 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),11507 .@"union", .@"struct" => item_ty.backingIntType(zcu),
11320 else => unreachable,11508 else => unreachable,
11321 };11509 };
11322 const min_int = try int_ty.minInt(pt, int_ty);11510 const min_int = try int_ty.minInt(pt, int_ty);
...@@ -11938,14 +12126,7 @@ fn analyzeSwitchCaptures(...@@ -11938,14 +12126,7 @@ fn analyzeSwitchCaptures(
11938 try sema.analyzeUnreachable(case_block, operand_src, false);12126 try sema.analyzeUnreachable(case_block, operand_src, false);
11939 break :payload_ref .unreachable_value;12127 break :payload_ref .unreachable_value;
11940 };12128 };
11941 if (sema.resolveValue(loaded_operand)) |err_val| {12129 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
11942 break :payload_ref .fromIntern(try pt.intern(.{ .err = .{
11943 .ty = capture_err_ty.toIntern(),
11944 .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name,
11945 } }));
11946 } else {
11947 break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand);
11948 }
11949 },12130 },
11950 .item_refs => |item_refs| {12131 .item_refs => |item_refs| {
11951 var names: InferredErrorSet.NameMap = .{};12132 var names: InferredErrorSet.NameMap = .{};
...@@ -11955,14 +12136,7 @@ fn analyzeSwitchCaptures(...@@ -11955,14 +12136,7 @@ fn analyzeSwitchCaptures(
11955 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});12136 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});
11956 }12137 }
11957 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());12138 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());
11958 if (sema.resolveValue(loaded_operand)) |err_val| {12139 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
11959 break :payload_ref .fromIntern(try pt.intern(.{ .err = .{
11960 .ty = capture_err_ty.toIntern(),
11961 .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name,
11962 } }));
11963 } else {
11964 break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand);
11965 }
11966 },12140 },
11967 }12141 }
11968 }12142 }
...@@ -12443,7 +12617,7 @@ fn validateSwitchItemOrRange(...@@ -12443,7 +12617,7 @@ fn validateSwitchItemOrRange(
12443 .first = .fromInterned(backing_int_val),12617 .first = .fromInterned(backing_int_val),
12444 .last = .fromInterned(backing_int_val),12618 .last = .fromInterned(backing_int_val),
12445 .src = item_src,12619 .src = item_src,
12446 }, item_ty.bitpackBackingInt(zcu), zcu);12620 }, item_ty.backingIntType(zcu), zcu);
12447 },12621 },
12448 .enum_literal, .@"fn", .pointer, .type => {12622 .enum_literal, .@"fn", .pointer, .type => {
12449 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|
...@@ -18400,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -18400,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
18400 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);
1840118575
18402 if (union_ty.containerLayout(zcu) == .@"packed") {18576 if (union_ty.containerLayout(zcu) == .@"packed") {
18403 return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node));18577 return sema.bitCastUnchecked(block, union_ty, payload);
18404 }18578 }
1840518579
18406 if (sema.resolveValue(payload)) |payload_val| {18580 if (sema.resolveValue(payload)) |payload_val| {
...@@ -18537,7 +18711,7 @@ fn zirStructInit(...@@ -18537,7 +18711,7 @@ fn zirStructInit(
18537 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);
1853818712
18539 if (resolved_ty.containerLayout(zcu) == .@"packed") {18713 if (resolved_ty.containerLayout(zcu) == .@"packed") {
18540 const union_val = try sema.bitCast(block, resolved_ty, init_inst, src);18714 const union_val = try sema.bitCastUnchecked(block, resolved_ty, init_inst);
18541 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);
18542 if (is_ref) {18716 if (is_ref) {
18543 return sema.analyzeRef(block, src, result_val, .none);18717 return sema.analyzeRef(block, src, result_val, .none);
...@@ -29716,41 +29890,29 @@ fn storePtrVal(...@@ -29716,41 +29890,29 @@ fn storePtrVal(
29716 }29890 }
29717}29891}
2971829892
29719fn bitCast(29893/// Asserts that the layout of `dest_ty` is already resolved.
29894fn bitCastUnchecked(
29720 sema: *Sema,29895 sema: *Sema,
29721 block: *Block,29896 block: *Block,
29722 dest_ty: Type,29897 dest_ty: Type,
29723 inst: Air.Inst.Ref,29898 inst: Air.Inst.Ref,
29724 inst_src: LazySrcLoc,
29725) CompileError!Air.Inst.Ref {29899) CompileError!Air.Inst.Ref {
29726 const pt = sema.pt;29900 const zcu = sema.pt.zcu;
29727 const zcu = pt.zcu;
29728 const old_ty = sema.typeOf(inst);29901 const old_ty = sema.typeOf(inst);
2972929902
29730 old_ty.assertHasLayout(zcu);29903 old_ty.assertHasLayout(zcu);
29731 try sema.ensureLayoutResolved(dest_ty, inst_src, .init);29904 dest_ty.assertHasLayout(zcu);
2973229905
29733 assert(old_ty.hasBitRepresentation(zcu));29906 assert(old_ty.hasBitRepresentation(zcu));
29734 assert(dest_ty.hasBitRepresentation(zcu));29907 assert(dest_ty.hasBitRepresentation(zcu));
29735 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);29908 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
29736 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);29909 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
2973729910 assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu));
29738 const dest_bits = dest_ty.bitSize(zcu);
29739 const old_bits = old_ty.bitSize(zcu);
29740
29741 if (old_bits != dest_bits) {
29742 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
29743 dest_ty.fmt(pt),
29744 dest_bits,
29745 old_ty.fmt(pt),
29746 old_bits,
29747 });
29748 }
2974929911
29750 if (sema.resolveValue(inst)) |val| {29912 if (sema.resolveValue(inst)) |val| {
29751 return .fromValue(try sema.bitCastVal(val, dest_ty));29913 return .fromValue(try sema.bitCastVal(val, dest_ty));
29752 }29914 }
29753 try sema.validateRuntimeValue(block, inst_src, inst);29915
29754 return block.addTyOp(.bit_cast, dest_ty, inst);29916 return block.addTyOp(.bit_cast, dest_ty, inst);
29755}29917}
2975629918
...@@ -29774,6 +29936,26 @@ pub fn bitCastVal(...@@ -29774,6 +29936,26 @@ pub fn bitCastVal(
29774 }29936 }
29775}29937}
2977629938
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
29777fn checkSpirvSliceAllowed(29959fn checkSpirvSliceAllowed(
29778 sema: *Sema,29960 sema: *Sema,
29779 block: *Block,29961 block: *Block,
...@@ -33942,14 +34124,6 @@ fn intFromFloatScalar(...@@ -33942,14 +34124,6 @@ fn intFromFloatScalar(
33942 return pt.getCoerced(cti_result, int_ty);34124 return pt.getCoerced(cti_result, int_ty);
33943}34125}
3394434126
33945fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
33946 const pt = sema.pt;
33947 if (!int_val.compareAllWithZero(.gte, pt.zcu)) return false;
33948 const end_val = try pt.intValue(tag_ty, end);
33949 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
33950 return true;
33951}
33952
33953/// Asserts the type is an exhaustive enum.34127/// Asserts the type is an exhaustive enum.
33954fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {34128fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
33955 const pt = sema.pt;34129 const pt = sema.pt;
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+1-1
...@@ -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 }
src/Type.zig+27-27
...@@ -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,15 +3057,12 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {...@@ -3058,15 +3057,12 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
3058 .one, .many, .c => .pointer,3057 .one, .many, .c => .pointer,
3059 },3058 },
30603059
3061 .@"enum" => {3060 .@"enum" => switch (ty.backingIntMode(zcu)) {
3062 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());3061 .explicit => switch (ty.backingIntType(zcu).toIntern()) {
3063 return switch (enum_obj.int_tag_mode) {3062 else => null,
3064 .explicit => if (enum_obj.int_tag_type != .noreturn_type)3063 .noreturn_type => .other,
3065 null3064 },
3066 else3065 .auto => .{ .enum_inferred_int_tag = ty },
3067 .other,
3068 .auto => .{ .enum_inferred_int_tag = ty },
3069 };
3070 },3066 },
30713067
3072 .@"struct" => switch (ty.containerLayout(zcu)) {3068 .@"struct" => switch (ty.containerLayout(zcu)) {
...@@ -3231,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {...@@ -3231,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {
3231 .float,3227 .float,
3232 => true,3228 => true,
32333229
3234 .@"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 },
3235 .pointer, .optional => ty.isPtrAtRuntime(zcu),3235 .pointer, .optional => ty.isPtrAtRuntime(zcu),
3236 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",3236 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
32373237
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+5-3
...@@ -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,
...@@ -5134,7 +5136,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {...@@ -5134,7 +5136,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
5134 return .{ .imm32 = 0xaaaaaaaa };5136 return .{ .imm32 = 0xaaaaaaaa };
5135 },5137 },
5136 .@"struct", .@"union" => {5138 .@"struct", .@"union" => {
5137 const backing_int_ty = ty.bitpackBackingInt(zcu);5139 const backing_int_ty = ty.backingIntType(zcu);
5138 return cg.emitUndefined(backing_int_ty);5140 return cg.emitUndefined(backing_int_ty);
5139 },5141 },
5140 else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}),5142 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/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;
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+378
...@@ -0,0 +1,378 @@
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 const v4: S4 = @fromBackingInt(b4);
255 try expect(v4 == S4.expected.val);
256
257 const v5: S5 = @fromBackingInt(b5);
258 try expect(v5 == S5.expected.val);
259 }
260 };
261 try static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int);
262 try comptime static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int);
263}
264
265const U1 = packed union(u8) {
266 a: u8,
267 b: i8,
268 const expected = .{
269 .val = @as(U1, .{ .b = -123 }),
270 .int = @as(@typeInfo(U1).@"union".backing_integer.?, @bitCast(@as(i8, -123))),
271 };
272};
273const U2 = packed union(i20) {
274 a: u20,
275 b: enum(i20) { x, y, z },
276 const expected = .{
277 .val = @as(U2, .{ .b = .z }),
278 .int = @as(@typeInfo(U2).@"union".backing_integer.?, 0b00000000000000000000000000000010),
279 };
280};
281const U3 = packed union(i32) {
282 a: packed struct(u32) {
283 x: u18,
284 y: i14,
285 },
286 b: packed union(u32) {
287 x: i32,
288 y: enum(u32) { u, v },
289 },
290 const expected = .{
291 .val = @as(U3, .{ .b = .{ .y = .v } }),
292 .int = @as(@typeInfo(U3).@"union".backing_integer.?, 0b00000000000000000000000000000001),
293 };
294};
295const U4 = packed union(i200) {
296 a: u200,
297 const expected = .{
298 .val = @as(U4, .{ .a = (1 << 199) + 10 }),
299 .int = @as(@typeInfo(U4).@"union".backing_integer.?, @bitCast(@as(u200, (1 << 199) + 10))),
300 };
301};
302const U5 = packed union(u0) {
303 a: u0,
304 const expected = .{
305 .val = @as(U5, .{ .a = 0 }),
306 .int = @as(@typeInfo(U5).@"union".backing_integer.?, 0),
307 };
308};
309
310test "@backingInt with packed unions" {
311 const static = struct {
312 fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void {
313 const b1 = @backingInt(v1);
314 comptime assert(@TypeOf(b1) == @typeInfo(U1).@"union".backing_integer.?);
315 try expect(b1 == U1.expected.int);
316
317 const b2 = @backingInt(v2);
318 comptime assert(@TypeOf(b2) == @typeInfo(U2).@"union".backing_integer.?);
319 try expect(b2 == U2.expected.int);
320
321 const b3 = @backingInt(v3);
322 comptime assert(@TypeOf(b3) == @typeInfo(U3).@"union".backing_integer.?);
323 try expect(b3 == U3.expected.int);
324
325 const b4 = @backingInt(v4);
326 comptime assert(@TypeOf(b4) == @typeInfo(U4).@"union".backing_integer.?);
327 try expect(b4 == U4.expected.int);
328
329 const b5 = @backingInt(v5);
330 comptime assert(@TypeOf(b5) == @typeInfo(U5).@"union".backing_integer.?);
331 try expect(b5 == U5.expected.int);
332 }
333 };
334 try static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val);
335 try comptime static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val);
336}
337
338test "@fromBackingInt with packed unions" {
339 const static = struct {
340 fn doTheTest(
341 b1: @typeInfo(U1).@"union".backing_integer.?,
342 b2: @typeInfo(U2).@"union".backing_integer.?,
343 b3: @typeInfo(U3).@"union".backing_integer.?,
344 b4: @typeInfo(U4).@"union".backing_integer.?,
345 b5: @typeInfo(U5).@"union".backing_integer.?,
346 ) !void {
347 const v1: U1 = @fromBackingInt(b1);
348 try expect(v1 == U1.expected.val);
349
350 const v2: U2 = @fromBackingInt(b2);
351 try expect(v2 == U2.expected.val);
352
353 const v3: U3 = @fromBackingInt(b3);
354 try expect(v3 == U3.expected.val);
355
356 const v4: U4 = @fromBackingInt(b4);
357 try expect(v4 == U4.expected.val);
358
359 const v5: U5 = @fromBackingInt(b5);
360 try expect(v5 == U5.expected.val);
361 }
362 };
363 try static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int);
364 try comptime static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int);
365}
366
367test "@fromBackingInt provides result type to its argument" {
368 const E = enum(u32) { a, b, c };
369 const static = struct {
370 fn doTheTest(x: u8) !void {
371 const e: E = @fromBackingInt(x);
372 try expect(e == .b);
373 try expect(@backingInt(e) == x);
374 }
375 };
376 try static.doTheTest(1);
377 try comptime static.doTheTest(1);
378}
test/behavior/enum.zig+15
...@@ -1465,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" {...@@ -1465,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" {
1465 try expect(val == .b);1465 try expect(val == .b);
1466 try expect(@intFromEnum(val) == 1);1466 try expect(@intFromEnum(val) == 1);
1467}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+23-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,18 @@ test "initialize packed struct field to undefined at comptime" {...@@ -1246,3 +1246,18 @@ 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}
test/behavior/packed-union.zig+14
...@@ -227,3 +227,17 @@ test "initialize packed union field to undefined at comptime" {...@@ -227,3 +227,17 @@ 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}
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+10-2
...@@ -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(noreturn, .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/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/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/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