| author | |
| committer | |
| log | 115e0894dbd39e8baa15f9b883105697a8af201b |
| tree | 1e4e9d2a82a4de4ce86ed5e4d28b23b37c129017 |
| parent | 58a94eaae24e955d4a4a69238617e1bdd90270e7 |
| parent | b203181a8a12c3cef0d9e11269c777bf677cb5aa |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/3596674 files changed, 1824 insertions(+), 367 deletions(-)
doc/langref.html.in+62-14| ... | @@ -2271,6 +2271,7 @@ or | ... | @@ -2271,6 +2271,7 @@ or |
| 2271 | This even works at {#link|comptime#}: | 2271 | This even works at {#link|comptime#}: |
| 2272 | </p> | 2272 | </p> |
| 2273 | {#code|test_packed_structs.zig#} | 2273 | {#code|test_packed_structs.zig#} |
| 2274 | |||
| 2274 | <p> | 2275 | <p> |
| 2275 | The backing integer can be inferred or explicitly provided. When | 2276 | The backing integer can be inferred or explicitly provided. When |
| 2276 | inferred, it will be unsigned. When explicitly provided, its bit width | 2277 | 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#} |
| 2281 | 2282 | ||
| 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#} |
| 2403 | 2410 | ||
| 2404 | {#see_also|@typeInfo|@tagName|@sizeOf#} | 2411 | {#see_also|@backingInt|@fromBackingInt|@typeInfo|@tagName|@sizeOf|noreturn#} |
| 2405 | 2412 | ||
| 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 semantics | 2441 | {#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#} |
| 2462 | 2467 | ||
| 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#} |
| 2496 | 2502 | ||
| ... | @@ -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#} |
| 2523 | 2529 | ||
| 2530 | {#see_also|@backingInt|@fromBackingInt#} | ||
| 2524 | {#header_close#} | 2531 | {#header_close#} |
| 2525 | 2532 | ||
| 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#} |
| 4434 | 4441 | ||
| 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#} |
| 4458 | 4487 | ||
| 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#} |
| 4955 | 4987 | ||
| 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#} |
| 5082 | 5129 | ||
| 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#} |
| 5285 | 5333 | ||
| 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | const Foo = enum { | 3 | const Foo = enum(u2) { |
| 4 | a, | 4 | a, |
| 5 | b, | 5 | b, |
| 6 | c, | 6 | c, |
| 7 | }; | 7 | }; |
| 8 | 8 | ||
| 9 | pub fn main() void { | 9 | fn 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 | } |
| 15 | 13 | ||
| 14 | pub fn main() void { | ||
| 15 | foo(3); | ||
| 16 | } | ||
| 17 | |||
| 16 | // exe=fail | 18 | // exe=fail |
doc/langref/test_comptime_invalid_enum_cast.zig+2-2| ... | @@ -1,11 +1,11 @@ | ... | @@ -1,11 +1,11 @@ |
| 1 | const Foo = enum { | 1 | const Foo = enum(u2) { |
| 2 | a, | 2 | a, |
| 3 | b, | 3 | b, |
| 4 | c, | 4 | c, |
| 5 | }; | 5 | }; |
| 6 | comptime { | 6 | comptime { |
| 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 | } |
| 11 | 11 |
doc/langref/test_enums.zig+17-11| ... | @@ -22,9 +22,9 @@ const Value = enum(u2) { | ... | @@ -22,9 +22,9 @@ const Value = enum(u2) { |
| 22 | // Now you can cast between u2 and Value. | 22 | // Now you can cast between u2 and Value. |
| 23 | // The ordinal value starts from 0, counting up by 1 from the previous member. | 23 | // The ordinal value starts from 0, counting up by 1 from the previous member. |
| 24 | test "enum ordinal value" { | 24 | test "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 | } |
| 29 | 29 | ||
| 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 | }; |
| 36 | test "set enum ordinal value" { | 36 | test "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 | } |
| 41 | 41 | ||
| 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 | }; |
| 50 | test "enum implicit ordinal values and overridden values" { | 50 | test "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 | } |
| 57 | 57 | ||
| 58 | // Enums can have methods, the same as structs and unions. | 58 | // Enums can have methods, the same as structs and unions. |
| ... | @@ -111,4 +111,10 @@ test "@tagName" { | ... | @@ -111,4 +111,10 @@ test "@tagName" { |
| 111 | try expectEqualStrings(@tagName(Small.three), "three"); | 111 | try expectEqualStrings(@tagName(Small.three), "three"); |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | // Empty enums are uninstantiable, their tag type is always noreturn. | ||
| 115 | const Empty = enum {}; | ||
| 116 | test "empty enum" { | ||
| 117 | try expectEqual(noreturn, @typeInfo(Empty).@"enum".tag_type); | ||
| 118 | } | ||
| 119 | |||
| 114 | // test | 120 | // test |
doc/langref/test_inline_else.zig+2-2| ... | @@ -9,7 +9,7 @@ const SliceTypeB = extern struct { | ... | @@ -9,7 +9,7 @@ const SliceTypeB = extern struct { |
| 9 | ptr: [*]SliceTypeA, | 9 | ptr: [*]SliceTypeA, |
| 10 | len: usize, | 10 | len: usize, |
| 11 | }; | 11 | }; |
| 12 | const AnySlice = union(enum) { | 12 | const 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 as | 23 | // With `inline for` the function gets generated as |
| 24 | // a series of `if` statements relying on the optimizer | 24 | // 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const expectEqual = std.testing.expectEqual; | ||
| 4 | |||
| 5 | const PackedStruct = packed struct(u8) { | ||
| 6 | lo: u4, | ||
| 7 | hi: u4, | ||
| 8 | }; | ||
| 9 | |||
| 10 | test "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)) { |
| 8 | 8 | ||
| 9 | test "tag values" { | 9 | test "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)); |
| 12 | 12 | ||
| 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 | } |
| 16 | 16 | ||
| 17 | // test | 17 | // test |
lib/std/meta.zig+35-3| ... | @@ -408,7 +408,8 @@ pub fn FieldEnum(comptime T: type) type { | ... | @@ -408,7 +408,8 @@ pub fn FieldEnum(comptime T: type) type { |
| 408 | else => {}, | 408 | else => {}, |
| 409 | } | 409 | } |
| 410 | 410 | ||
| 411 | const IntTag = std.math.IntFittingRange(0, field_names.len -| 1); | 411 | if (field_names.len == 0) return enum {}; |
| 412 | const IntTag = std.math.IntFittingRange(0, field_names.len - 1); | ||
| 412 | return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len)); | 413 | return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len)); |
| 413 | } | 414 | } |
| 414 | 415 | ||
| ... | @@ -469,7 +470,8 @@ test FieldEnum { | ... | @@ -469,7 +470,8 @@ test FieldEnum { |
| 469 | 470 | ||
| 470 | pub fn DeclEnum(comptime T: type) type { | 471 | pub fn DeclEnum(comptime T: type) type { |
| 471 | const decl_names = declarations(T); | 472 | const decl_names = declarations(T); |
| 472 | const IntTag = std.math.IntFittingRange(0, decl_names.len -| 1); | 473 | if (decl_names.len == 0) return enum {}; |
| 474 | const IntTag = std.math.IntFittingRange(0, decl_names.len - 1); | ||
| 473 | return @Enum(IntTag, .exhaustive, decl_names, &std.simd.iota(IntTag, decl_names.len)); | 475 | return @Enum(IntTag, .exhaustive, decl_names, &std.simd.iota(IntTag, decl_names.len)); |
| 474 | } | 476 | } |
| 475 | 477 | ||
| ... | @@ -504,10 +506,40 @@ pub fn BareUnion(comptime T: type) type { | ... | @@ -504,10 +506,40 @@ pub fn BareUnion(comptime T: type) type { |
| 504 | .@"union" => |u| u, | 506 | .@"union" => |u| u, |
| 505 | else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"), | 507 | else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"), |
| 506 | }; | 508 | }; |
| 507 | |||
| 508 | return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]); | 509 | return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]); |
| 509 | } | 510 | } |
| 510 | 511 | ||
| 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. | ||
| 514 | pub 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 | |||
| 529 | test 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 | |||
| 511 | pub fn Tag(comptime T: type) type { | 543 | pub fn Tag(comptime T: type) type { |
| 512 | return switch (@typeInfo(T)) { | 544 | return switch (@typeInfo(T)) { |
| 513 | .@"enum" => |info| info.tag_type, | 545 | .@"enum" => |info| info.tag_type, |
lib/std/zig.zig-2| ... | @@ -906,7 +906,6 @@ pub const SimpleComptimeReason = enum(u32) { | ... | @@ -906,7 +906,6 @@ pub const SimpleComptimeReason = enum(u32) { |
| 906 | slice_single_item_ptr_bounds, | 906 | slice_single_item_ptr_bounds, |
| 907 | stored_to_comptime_field, | 907 | stored_to_comptime_field, |
| 908 | stored_to_comptime_var, | 908 | stored_to_comptime_var, |
| 909 | casted_to_comptime_enum, | ||
| 910 | casted_to_comptime_int, | 909 | casted_to_comptime_int, |
| 911 | casted_to_comptime_float, | 910 | casted_to_comptime_float, |
| 912 | std_lang_decl, | 911 | std_lang_decl, |
| ... | @@ -989,7 +988,6 @@ pub const SimpleComptimeReason = enum(u32) { | ... | @@ -989,7 +988,6 @@ pub const SimpleComptimeReason = enum(u32) { |
| 989 | .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds", | 988 | .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds", |
| 990 | .stored_to_comptime_field => "value stored to a comptime field must be comptime-known", | 989 | .stored_to_comptime_field => "value stored to a comptime field must be comptime-known", |
| 991 | .stored_to_comptime_var => "value stored to a comptime variable must be comptime-known", | 990 | .stored_to_comptime_var => "value stored to a comptime variable must be comptime-known", |
| 992 | .casted_to_comptime_enum => "value casted to enum with 'comptime_int' tag type must be comptime-known", | ||
| 993 | .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known", | 991 | .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known", |
| 994 | .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known", | 992 | .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known", |
| 995 | .std_lang_decl => "'std.lang' declaration values must be comptime-known", | 993 | .std_lang_decl => "'std.lang' declaration values must be comptime-known", |
lib/std/zig/Ast/Render.zig+19-2| ... | @@ -1652,7 +1652,17 @@ fn renderBuiltinCall( | ... | @@ -1652,7 +1652,17 @@ fn renderBuiltinCall( |
| 1652 | const tree = r.tree; | 1652 | const tree = r.tree; |
| 1653 | const ais = r.ais; | 1653 | const ais = r.ais; |
| 1654 | 1654 | ||
| 1655 | try renderToken(r, builtin_token, .none); // @name | 1655 | // remove before 0.18.0 is released |
| 1656 | const builtin_token_slice = tree.tokenSlice(builtin_token); // @name | ||
| 1657 | const lexeme: []const u8, const have_int_cast: bool = lexeme: { | ||
| 1658 | if (mem.eql(u8, builtin_token_slice, "@intFromEnum")) | ||
| 1659 | break :lexeme .{ "@backingInt", false }; | ||
| 1660 | if (mem.eql(u8, builtin_token_slice, "@enumFromInt")) | ||
| 1661 | break :lexeme .{ "@fromBackingInt(@intCast", true }; | ||
| 1662 | break :lexeme .{ builtin_token_slice, false }; | ||
| 1663 | }; | ||
| 1664 | try ais.writeAll(lexeme); | ||
| 1665 | try renderSpace(r, builtin_token, builtin_token_slice.len, .none); | ||
| 1656 | 1666 | ||
| 1657 | if (r.fixups.rebase_imported_paths) |prefix| { | 1667 | if (r.fixups.rebase_imported_paths) |prefix| { |
| 1658 | const slice = tree.tokenSlice(builtin_token); | 1668 | const slice = tree.tokenSlice(builtin_token); |
| ... | @@ -1675,7 +1685,14 @@ fn renderBuiltinCall( | ... | @@ -1675,7 +1685,14 @@ fn renderBuiltinCall( |
| 1675 | } | 1685 | } |
| 1676 | } | 1686 | } |
| 1677 | 1687 | ||
| 1678 | return renderParamList(r, builtin_token + 1, params, space); | 1688 | try renderParamList(r, builtin_token + 1, params, .skip); // space is rendered below |
| 1689 | if (have_int_cast) try ais.writeAll(")"); | ||
| 1690 | const rparen: Ast.TokenIndex = rparen: { | ||
| 1691 | if (params.len == 0) break :rparen builtin_token + 1 + 1; | ||
| 1692 | const after_last_param_tok = tree.lastToken(params[params.len - 1]) + 1; | ||
| 1693 | break :rparen after_last_param_tok + @intFromBool(tree.tokenTag(after_last_param_tok) == .comma); | ||
| 1694 | }; | ||
| 1695 | return renderSpace(r, rparen, tokenSliceForRender(tree, rparen).len, space); | ||
| 1679 | } | 1696 | } |
| 1680 | 1697 | ||
| 1681 | fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex { | 1698 | fn fnProtoRparen(tree: Ast, fn_proto: Ast.full.FnProto, maybe_bang: Ast.TokenIndex) Ast.TokenIndex { |
lib/std/zig/AstGen.zig+18| ... | @@ -2703,6 +2703,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2703,6 +2703,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2703 | .elem_type, | 2703 | .elem_type, |
| 2704 | .indexable_ptr_elem_type, | 2704 | .indexable_ptr_elem_type, |
| 2705 | .splat_op_result_ty, | 2705 | .splat_op_result_ty, |
| 2706 | .from_backing_int_arg_ty, | ||
| 2706 | .reify_int, | 2707 | .reify_int, |
| 2707 | .vector_type, | 2708 | .vector_type, |
| 2708 | .indexable_ptr_len, | 2709 | .indexable_ptr_len, |
| ... | @@ -2803,6 +2804,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2803,6 +2804,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2803 | .error_set_decl, | 2804 | .error_set_decl, |
| 2804 | .enum_from_int, | 2805 | .enum_from_int, |
| 2805 | .int_from_enum, | 2806 | .int_from_enum, |
| 2807 | .backing_int, | ||
| 2808 | .from_backing_int, | ||
| 2806 | .type_info, | 2809 | .type_info, |
| 2807 | .size_of, | 2810 | .size_of, |
| 2808 | .bit_size_of, | 2811 | .bit_size_of, |
| ... | @@ -9168,6 +9171,7 @@ fn builtinCall( | ... | @@ -9168,6 +9171,7 @@ fn builtinCall( |
| 9168 | .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota), | 9171 | .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota), |
| 9169 | .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum), | 9172 | .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum), |
| 9170 | .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool), | 9173 | .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool), |
| 9174 | .backing_int => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .backing_int), | ||
| 9171 | .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file), | 9175 | .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file), |
| 9172 | .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name), | 9176 | .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name), |
| 9173 | .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety), | 9177 | .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety), |
| ... | @@ -9199,6 +9203,20 @@ fn builtinCall( | ... | @@ -9199,6 +9203,20 @@ fn builtinCall( |
| 9199 | .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name), | 9203 | .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name), |
| 9200 | // zig fmt: on | 9204 | // zig fmt: on |
| 9201 | 9205 | ||
| 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 is | 294 | /// 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 compile | 295 | /// 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 type | 880 | /// 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 causing | 951 | /// 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 stacked | 2184 | /// 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, |
| 2166 | 2189 | ||
| 2167 | pub const InstData = struct { | 2190 | pub const InstData = struct { |
| ... | @@ -4141,6 +4164,7 @@ fn findTrackableInner( | ... | @@ -4141,6 +4164,7 @@ fn findTrackableInner( |
| 4141 | .elem_type, | 4164 | .elem_type, |
| 4142 | .indexable_ptr_elem_type, | 4165 | .indexable_ptr_elem_type, |
| 4143 | .splat_op_result_ty, | 4166 | .splat_op_result_ty, |
| 4167 | .from_backing_int_arg_ty, | ||
| 4144 | .indexable_ptr_len, | 4168 | .indexable_ptr_len, |
| 4145 | .anyframe_type, | 4169 | .anyframe_type, |
| 4146 | .as_node, | 4170 | .as_node, |
| ... | @@ -4296,6 +4320,8 @@ fn findTrackableInner( | ... | @@ -4296,6 +4320,8 @@ fn findTrackableInner( |
| 4296 | .float_from_int, | 4320 | .float_from_int, |
| 4297 | .ptr_from_int, | 4321 | .ptr_from_int, |
| 4298 | .enum_from_int, | 4322 | .enum_from_int, |
| 4323 | .backing_int, | ||
| 4324 | .from_backing_int, | ||
| 4299 | .float_cast, | 4325 | .float_cast, |
| 4300 | .int_cast, | 4326 | .int_cast, |
| 4301 | .ptr_cast, | 4327 | .ptr_cast, |
lib/std/zig/parser_test.zig+61-1| ... | @@ -1120,7 +1120,7 @@ test "zig fmt: empty enum decls" { | ... | @@ -1120,7 +1120,7 @@ test "zig fmt: empty enum decls" { |
| 1120 | \\const A = enum {}; | 1120 | \\const A = enum {}; |
| 1121 | \\const B = enum(u32) {}; | 1121 | \\const B = enum(u32) {}; |
| 1122 | \\const C = extern enum(c_int) {}; | 1122 | \\const C = extern enum(c_int) {}; |
| 1123 | \\const D = packed enum(u8) {}; | 1123 | \\const D = packed enum(noreturn) {}; |
| 1124 | \\ | 1124 | \\ |
| 1125 | ); | 1125 | ); |
| 1126 | } | 1126 | } |
| ... | @@ -6938,6 +6938,66 @@ test "zig fmt: inner over-indented if expressions becoming multiline" { | ... | @@ -6938,6 +6938,66 @@ test "zig fmt: inner over-indented if expressions becoming multiline" { |
| 6938 | ); | 6938 | ); |
| 6939 | } | 6939 | } |
| 6940 | 6940 | ||
| 6941 | test "zig fmt: canonicalize @intFromEnum(x) to @backingInt(x)" { | ||
| 6942 | try testTransform( | ||
| 6943 | \\const a = @intFromEnum(x); | ||
| 6944 | \\ | ||
| 6945 | \\const b = @intFromEnum( | ||
| 6946 | \\ x, | ||
| 6947 | \\); | ||
| 6948 | \\ | ||
| 6949 | \\const c = @intFromEnum(x); // comment preserved | ||
| 6950 | \\ | ||
| 6951 | \\const d = @intFromEnum( // comment 1 preserved | ||
| 6952 | \\ x, // comment 2 preserved | ||
| 6953 | \\); // comment 3 preserved | ||
| 6954 | \\ | ||
| 6955 | , | ||
| 6956 | \\const a = @backingInt(x); | ||
| 6957 | \\ | ||
| 6958 | \\const b = @backingInt( | ||
| 6959 | \\ x, | ||
| 6960 | \\); | ||
| 6961 | \\ | ||
| 6962 | \\const c = @backingInt(x); // comment preserved | ||
| 6963 | \\ | ||
| 6964 | \\const d = @backingInt( // comment 1 preserved | ||
| 6965 | \\ x, // comment 2 preserved | ||
| 6966 | \\); // comment 3 preserved | ||
| 6967 | \\ | ||
| 6968 | ); | ||
| 6969 | } | ||
| 6970 | |||
| 6971 | test "zig fmt: canonicalize @enumFromInt(x) to @fromBackingInt(@intCast(x))" { | ||
| 6972 | try testTransform( | ||
| 6973 | \\const a: E = @enumFromInt(x); | ||
| 6974 | \\ | ||
| 6975 | \\const b: E = @enumFromInt( | ||
| 6976 | \\ x, | ||
| 6977 | \\); | ||
| 6978 | \\ | ||
| 6979 | \\const c: E = @enumFromInt(x); // comment preserved | ||
| 6980 | \\ | ||
| 6981 | \\const d: E = @enumFromInt( // comment 1 preserved | ||
| 6982 | \\ x, // comment 2 preserved | ||
| 6983 | \\); // comment 3 preserved | ||
| 6984 | \\ | ||
| 6985 | , | ||
| 6986 | \\const a: E = @fromBackingInt(@intCast(x)); | ||
| 6987 | \\ | ||
| 6988 | \\const b: E = @fromBackingInt(@intCast( | ||
| 6989 | \\ x, | ||
| 6990 | \\)); | ||
| 6991 | \\ | ||
| 6992 | \\const c: E = @fromBackingInt(@intCast(x)); // comment preserved | ||
| 6993 | \\ | ||
| 6994 | \\const d: E = @fromBackingInt(@intCast( // comment 1 preserved | ||
| 6995 | \\ x, // comment 2 preserved | ||
| 6996 | \\)); // comment 3 preserved | ||
| 6997 | \\ | ||
| 6998 | ); | ||
| 6999 | } | ||
| 7000 | |||
| 6941 | test "recovery: top level" { | 7001 | test "recovery: top level" { |
| 6942 | try testError( | 7002 | try testError( |
| 6943 | \\test "" {inline} | 7003 | \\test "" {inline} |
src/Air.zig+6| ... | @@ -288,6 +288,10 @@ pub const Inst = struct { | ... | @@ -288,6 +288,10 @@ pub const Inst = struct { |
| 288 | /// | 288 | /// |
| 289 | /// Uses the `ty_op` field. | 289 | /// Uses the `ty_op` field. |
| 290 | bit_cast, | 290 | bit_cast, |
| 291 | /// Like `bit_cast`, but triggers a safety panic if the destination type is an exhaustive | ||
| 292 | /// enum and the operand is not a valid value of this type; | ||
| 293 | /// i.e. equivalent to a safety check based on `.is_named_enum_value` | ||
| 294 | bit_cast_safe, | ||
| 291 | /// Cast a pointer to a different pointer type. The result type is a slice iff the operand | 295 | /// 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 attributes | 296 | /// 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) |
| 1717 | 1721 | ||
| 1718 | .not, | 1722 | .not, |
| 1719 | .bit_cast, | 1723 | .bit_cast, |
| 1724 | .bit_cast_safe, | ||
| 1720 | .ptr_cast, | 1725 | .ptr_cast, |
| 1721 | .ptr_from_int, | 1726 | .ptr_from_int, |
| 1722 | .int_from_ptr, | 1727 | .int_from_ptr, |
| ... | @@ -1969,6 +1974,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { | ... | @@ -1969,6 +1974,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1969 | .add_safe, | 1974 | .add_safe, |
| 1970 | .sub_safe, | 1975 | .sub_safe, |
| 1971 | .mul_safe, | 1976 | .mul_safe, |
| 1977 | .bit_cast_safe, | ||
| 1972 | .int_cast_safe, | 1978 | .int_cast_safe, |
| 1973 | .int_from_float_safe, | 1979 | .int_from_float_safe, |
| 1974 | .int_from_float_optimized_safe, | 1980 | .int_from_float_optimized_safe, |
src/Air/Legalize.zig+116-14| ... | @@ -156,6 +156,10 @@ pub const Feature = enum { | ... | @@ -156,6 +156,10 @@ pub const Feature = enum { |
| 156 | /// Legalize splat to a one element vector to a bitcast. | 156 | /// Legalize splat to a one element vector to a bitcast. |
| 157 | splat_one_elem_to_bit_cast, | 157 | splat_one_elem_to_bit_cast, |
| 158 | 158 | ||
| 159 | /// Replace `bit_cast_safe` with an explicit safety check which `call`s the panic function on failure. | ||
| 160 | /// `scalarize_*` variants for `bit_cast_safe` do not exist since the safety check is only desired if the result | ||
| 161 | /// type is a scalar enum type, so the scalarizatins for regular `bit_cast` are exactly equivalent. | ||
| 162 | expand_bit_cast_safe, | ||
| 159 | /// Replace `int_cast_safe` with an explicit safety check which `call`s the panic function on failure. | 163 | /// Replace `int_cast_safe` with an explicit safety check which `call`s the panic function on failure. |
| 160 | /// Not compatible with `scalarize_int_cast_safe`. | 164 | /// Not compatible with `scalarize_int_cast_safe`. |
| 161 | expand_int_cast_safe, | 165 | expand_int_cast_safe, |
| ... | @@ -600,9 +604,28 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -600,9 +604,28 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 600 | continue :inst l.replaceInst(inst, .block, payload); | 604 | continue :inst l.replaceInst(inst, .block, payload); |
| 601 | } | 605 | } |
| 602 | }, | 606 | }, |
| 607 | .bit_cast_safe => if (l.features.has(.expand_bit_cast_safe)) { | ||
| 608 | if (try l.safeBitcastBlockPayload(inst)) |payload| { | ||
| 609 | continue :inst l.replaceInst(inst, .block, payload); | ||
| 610 | } | ||
| 611 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 612 | continue :inst l.replaceInst(inst, .bit_cast, .{ .ty_op = ty_op }); | ||
| 613 | } else if (l.features.hasAny(&.{ | ||
| 614 | .scalarize_bit_cast_array, | ||
| 615 | .scalarize_bit_cast_vector_non_elementwise, | ||
| 616 | .scalarize_bit_cast_padded_elems, | ||
| 617 | })) { | ||
| 618 | if (try l.scalarizeBitcastBlockPayload(inst)) |payload| { | ||
| 619 | continue :inst l.replaceInst(inst, .block, payload); | ||
| 620 | } | ||
| 621 | }, | ||
| 603 | .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) { | 622 | .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) { |
| 604 | assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both | 623 | assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both |
| 605 | continue :inst l.replaceInst(inst, .block, try l.safeIntcastBlockPayload(inst)); | 624 | if (try l.safeIntcastBlockPayload(inst)) |payload| { |
| 625 | continue :inst l.replaceInst(inst, .block, payload); | ||
| 626 | } | ||
| 627 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | ||
| 628 | continue :inst l.replaceInst(inst, .int_cast, .{ .ty_op = ty_op }); | ||
| 606 | } else if (l.features.has(.scalarize_int_cast_safe)) { | 629 | } else if (l.features.has(.scalarize_int_cast_safe)) { |
| 607 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; | 630 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 608 | if (ty_op.ty.toType().isVector(zcu)) { | 631 | if (ty_op.ty.toType().isVector(zcu)) { |
| ... | @@ -1006,7 +1029,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz | ... | @@ -1006,7 +1029,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz |
| 1006 | 1029 | ||
| 1007 | if (result_is_array) { | 1030 | if (result_is_array) { |
| 1008 | // This is only allowed when legalizing an elementwise bitcast. | 1031 | // This is only allowed when legalizing an elementwise bitcast. |
| 1009 | assert(orig.tag == .bit_cast); | 1032 | switch (orig.tag) { |
| 1033 | .bit_cast, .bit_cast_safe => {}, | ||
| 1034 | else => unreachable, | ||
| 1035 | } | ||
| 1010 | assert(form == .ty_op); | 1036 | assert(form == .ty_op); |
| 1011 | } | 1037 | } |
| 1012 | 1038 | ||
| ... | @@ -1076,7 +1102,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz | ... | @@ -1076,7 +1102,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz |
| 1076 | orig_operand, | 1102 | orig_operand, |
| 1077 | index_val, | 1103 | index_val, |
| 1078 | ).toRef(); | 1104 | ).toRef(); |
| 1079 | break :elem loop.block.addTyOp(l, orig.tag, res_elem_ty, operand).toRef(); | 1105 | const scalar_tag: Air.Inst.Tag = switch (orig.tag) { |
| 1106 | .bit_cast_safe => .bit_cast, // safety check is not supposed to be elementwise | ||
| 1107 | else => orig.tag, | ||
| 1108 | }; | ||
| 1109 | break :elem loop.block.addTyOp(l, scalar_tag, res_elem_ty, operand).toRef(); | ||
| 1080 | }, | 1110 | }, |
| 1081 | .bin_op => elem: { | 1111 | .bin_op => elem: { |
| 1082 | const orig_bin = orig.data.bin_op; | 1112 | const orig_bin = orig.data.bin_op; |
| ... | @@ -1700,9 +1730,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!? | ... | @@ -1700,9 +1730,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!? |
| 1700 | 1730 | ||
| 1701 | // Now convert `uint_ty` (`uN`) to `dest_ty`. | 1731 | // Now convert `uint_ty` (`uN`) to `dest_ty`. |
| 1702 | 1732 | ||
| 1733 | // We omit the safety check when casting to an array or a vector since it's | ||
| 1734 | // not supposed to be elementwise. | ||
| 1735 | if (dest_ty.zigTypeTag(zcu) == .@"enum") assert(int_to_dest_ok); | ||
| 1736 | |||
| 1703 | if (int_to_dest_ok) { | 1737 | if (int_to_dest_ok) { |
| 1704 | _ = main_block.stealCapacity(17); | 1738 | _ = main_block.stealCapacity(17); |
| 1705 | const result = main_block.addBitCast(l, dest_ty, uint_val); | 1739 | const result = switch (l.air_instructions.items(.tag)[@intFromEnum(orig_inst)]) { |
| 1740 | .bit_cast => main_block.addBitCast(l, dest_ty, uint_val), | ||
| 1741 | .bit_cast_safe => main_block.add(l, .{ | ||
| 1742 | .tag = .bit_cast_safe, | ||
| 1743 | .data = .{ .ty_op = .{ | ||
| 1744 | .ty = .fromType(dest_ty), | ||
| 1745 | .operand = uint_val, | ||
| 1746 | } }, | ||
| 1747 | }).toRef(), | ||
| 1748 | else => unreachable, | ||
| 1749 | }; | ||
| 1706 | main_block.addBr(l, orig_inst, result); | 1750 | main_block.addBr(l, orig_inst, result); |
| 1707 | } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) { | 1751 | } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) { |
| 1708 | _ = main_block.stealCapacity(16); | 1752 | _ = main_block.stealCapacity(16); |
| ... | @@ -2089,7 +2133,62 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize | ... | @@ -2089,7 +2133,62 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize |
| 2089 | } }; | 2133 | } }; |
| 2090 | } | 2134 | } |
| 2091 | 2135 | ||
| 2092 | fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { | 2136 | fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?Air.Inst.Data { |
| 2137 | const pt = l.pt; | ||
| 2138 | const zcu = pt.zcu; | ||
| 2139 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; | ||
| 2140 | |||
| 2141 | const operand_ref = ty_op.operand; | ||
| 2142 | const dest_ty = ty_op.ty.toType(); | ||
| 2143 | |||
| 2144 | if (dest_ty.zigTypeTag(zcu) != .@"enum" or | ||
| 2145 | dest_ty.isNonexhaustiveEnum(zcu) or | ||
| 2146 | !zcu.backendSupportsFeature(.is_named_enum_value)) | ||
| 2147 | { | ||
| 2148 | return null; | ||
| 2149 | } | ||
| 2150 | |||
| 2151 | // We are building this: | ||
| 2152 | // | ||
| 2153 | // %x = block({ | ||
| 2154 | // %1 = bit_cast(@res_ty, %y) | ||
| 2155 | // %2 = is_named_enum_value(%1) | ||
| 2156 | // %3 = cond_br(%2, { | ||
| 2157 | // %4 = br(%x, %1) | ||
| 2158 | // }, { | ||
| 2159 | // %5 = call(@panic.invalidEnumValue, []) | ||
| 2160 | // %6 = unreach() | ||
| 2161 | // }) | ||
| 2162 | // }) | ||
| 2163 | |||
| 2164 | var inst_buf: [6]Air.Inst.Index = undefined; | ||
| 2165 | try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len); | ||
| 2166 | |||
| 2167 | var block: Block = .init(&inst_buf); | ||
| 2168 | |||
| 2169 | const cast_inst = block.addBitCast(l, dest_ty, operand_ref); | ||
| 2170 | const is_named_inst = block.add(l, .{ | ||
| 2171 | .tag = .is_named_enum_value, | ||
| 2172 | .data = .{ .un_op = cast_inst }, | ||
| 2173 | }); | ||
| 2174 | |||
| 2175 | var condbr: CondBr = .init(l, is_named_inst.toRef(), &block, .{ .false = .cold }); | ||
| 2176 | |||
| 2177 | condbr.then_block = .init(block.stealRemainingCapacity()); | ||
| 2178 | condbr.then_block.addBr(l, orig_inst, cast_inst); | ||
| 2179 | |||
| 2180 | condbr.else_block = .init(condbr.then_block.stealRemainingCapacity()); | ||
| 2181 | try condbr.else_block.addPanic(l, .invalid_enum_value); | ||
| 2182 | |||
| 2183 | try condbr.finish(l); | ||
| 2184 | |||
| 2185 | return .{ .ty_pl = .{ | ||
| 2186 | .ty = .fromType(dest_ty), | ||
| 2187 | .payload = try l.addBlockBody(block.body()), | ||
| 2188 | } }; | ||
| 2189 | } | ||
| 2190 | |||
| 2191 | fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?Air.Inst.Data { | ||
| 2093 | const pt = l.pt; | 2192 | const pt = l.pt; |
| 2094 | const zcu = pt.zcu; | 2193 | const zcu = pt.zcu; |
| 2095 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; | 2194 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op; |
| ... | @@ -2108,6 +2207,9 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2108,6 +2207,9 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2108 | .@"enum" => true, | 2207 | .@"enum" => true, |
| 2109 | else => unreachable, | 2208 | else => unreachable, |
| 2110 | }; | 2209 | }; |
| 2210 | const have_enum_value_check = dest_is_enum and | ||
| 2211 | !dest_ty.isNonexhaustiveEnum(zcu) and | ||
| 2212 | zcu.backendSupportsFeature(.is_named_enum_value); | ||
| 2111 | 2213 | ||
| 2112 | const operand_info = operand_scalar_ty.intInfo(zcu); | 2214 | const operand_info = operand_scalar_ty.intInfo(zcu); |
| 2113 | const dest_info = dest_scalar_ty.intInfo(zcu); | 2215 | const dest_info = dest_scalar_ty.intInfo(zcu); |
| ... | @@ -2123,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2123,6 +2225,10 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2123 | }; | 2225 | }; |
| 2124 | }; | 2226 | }; |
| 2125 | 2227 | ||
| 2228 | if (!have_enum_value_check and !have_min_check and !have_max_check) { | ||
| 2229 | return null; | ||
| 2230 | } | ||
| 2231 | |||
| 2126 | // The worst-case scenario in terms of total instructions and total condbrs is the case where | 2232 | // The worst-case scenario in terms of total instructions and total condbrs is the case where |
| 2127 | // the result type is an exhaustive enum whose tag type is smaller than the operand type: | 2233 | // the result type is an exhaustive enum whose tag type is smaller than the operand type: |
| 2128 | // | 2234 | // |
| ... | @@ -2172,7 +2278,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2172,7 +2278,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2172 | const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds; | 2278 | const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds; |
| 2173 | 2279 | ||
| 2174 | if (have_min_check or have_max_check) { | 2280 | if (have_min_check or have_max_check) { |
| 2175 | const dest_int_ty = if (dest_is_enum) dest_ty.intTagType(zcu) else dest_ty; | 2281 | const dest_int_ty = if (dest_is_enum) dest_ty.backingIntType(zcu) else dest_ty; |
| 2176 | const condbr = &condbr_buf[condbr_idx]; | 2282 | const condbr = &condbr_buf[condbr_idx]; |
| 2177 | condbr_idx += 1; | 2283 | condbr_idx += 1; |
| 2178 | const below_min_inst: Air.Inst.Index = if (have_min_check) inst: { | 2284 | const below_min_inst: Air.Inst.Index = if (have_min_check) inst: { |
| ... | @@ -2218,7 +2324,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2218,7 +2324,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2218 | } }, | 2324 | } }, |
| 2219 | }); | 2325 | }); |
| 2220 | // For ints we're already done, but for exhaustive enums we must check this is a valid tag. | 2326 | // For ints we're already done, but for exhaustive enums we must check this is a valid tag. |
| 2221 | if (dest_is_enum and !dest_ty.isNonexhaustiveEnum(zcu) and zcu.backendSupportsFeature(.is_named_enum_value)) { | 2327 | if (have_enum_value_check) { |
| 2222 | assert(!is_vector); // vectors of enums don't exist | 2328 | assert(!is_vector); // vectors of enums don't exist |
| 2223 | // We are building this: | 2329 | // We are building this: |
| 2224 | // %1 = is_named_enum_value(%cast_inst) | 2330 | // %1 = is_named_enum_value(%cast_inst) |
| ... | @@ -2240,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In | ... | @@ -2240,16 +2346,12 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In |
| 2240 | cur_block = &condbr.then_block; | 2346 | cur_block = &condbr.then_block; |
| 2241 | } | 2347 | } |
| 2242 | // Finally, just `br` to our outer `block`. | 2348 | // Finally, just `br` to our outer `block`. |
| 2243 | _ = cur_block.add(l, .{ | 2349 | cur_block.addBr(l, orig_inst, cast_inst.toRef()); |
| 2244 | .tag = .br, | 2350 | |
| 2245 | .data = .{ .br = .{ | ||
| 2246 | .block_inst = orig_inst, | ||
| 2247 | .operand = cast_inst.toRef(), | ||
| 2248 | } }, | ||
| 2249 | }); | ||
| 2250 | // We might not have used all of the instructions; that's intentional. | 2351 | // We might not have used all of the instructions; that's intentional. |
| 2251 | _ = cur_block.stealRemainingCapacity(); | 2352 | _ = cur_block.stealRemainingCapacity(); |
| 2252 | 2353 | ||
| 2354 | assert(condbr_idx != 0); // should have already returned `null` | ||
| 2253 | for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l); | 2355 | for (condbr_buf[0..condbr_idx]) |*condbr| try condbr.finish(l); |
| 2254 | return .{ .ty_pl = .{ | 2356 | return .{ .ty_pl = .{ |
| 2255 | .ty = Air.internedToRef(dest_ty.toIntern()), | 2357 | .ty = Air.internedToRef(dest_ty.toIntern()), |
src/Air/Liveness.zig+1| ... | @@ -491,6 +491,7 @@ fn analyzeInst( | ... | @@ -491,6 +491,7 @@ fn analyzeInst( |
| 491 | 491 | ||
| 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 | // unary | 79 | // 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 | }, |
| 120 | 120 | ||
| 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 { |
| 231 | 231 | ||
| 232 | .not, | 232 | .not, |
| 233 | .bit_cast, | 233 | .bit_cast, |
| 234 | .bit_cast_safe, | ||
| 234 | .ptr_cast, | 235 | .ptr_cast, |
| 235 | .ptr_from_int, | 236 | .ptr_from_int, |
| 236 | .int_from_ptr, | 237 | .int_from_ptr, |
src/InternPool.zig+3-7| ... | @@ -5040,7 +5040,6 @@ pub const Tag = enum(u8) { | ... | @@ -5040,7 +5040,6 @@ pub const Tag = enum(u8) { |
| 5040 | /// The set of values that are encoded this way is: | 5040 | /// The set of values that are encoded this way is: |
| 5041 | /// * An array or vector which has length 0. | 5041 | /// * An array or vector which has length 0. |
| 5042 | /// * A struct which has all fields comptime-known. | 5042 | /// * A struct which has all fields comptime-known. |
| 5043 | /// * An empty enum or union. TODO: this value's existence is strange, because such a type in reality has no values. See #15909 | ||
| 5044 | /// data is Index of the type, which is known to be zero bits at runtime. | 5043 | /// data is Index of the type, which is known to be zero bits at runtime. |
| 5045 | only_possible_value, | 5044 | only_possible_value, |
| 5046 | /// data is extra index to Key.Union. | 5045 | /// data is extra index to Key.Union. |
| ... | @@ -7740,12 +7739,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: | ... | @@ -7740,12 +7739,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: |
| 7740 | }), | 7739 | }), |
| 7741 | 7740 | ||
| 7742 | .enum_tag => |enum_tag| { | 7741 | .enum_tag => |enum_tag| { |
| 7743 | assert(ip.isEnumType(enum_tag.ty)); | 7742 | const enum_obj = ip.loadEnumType(enum_tag.ty); |
| 7744 | switch (ip.indexToKey(enum_tag.ty)) { | 7743 | assert(ip.typeOf(enum_tag.int) == enum_obj.int_tag_type); |
| 7745 | .simple_type => assert(ip.isIntegerType(ip.typeOf(enum_tag.int))), | ||
| 7746 | .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).int_tag_type), | ||
| 7747 | else => unreachable, | ||
| 7748 | } | ||
| 7749 | items.appendAssumeCapacity(.{ | 7744 | items.appendAssumeCapacity(.{ |
| 7750 | .tag = .enum_tag, | 7745 | .tag = .enum_tag, |
| 7751 | .data = try addExtra(extra, enum_tag), | 7746 | .data = try addExtra(extra, enum_tag), |
| ... | @@ -10197,6 +10192,7 @@ pub fn getCoerced( | ... | @@ -10197,6 +10192,7 @@ pub fn getCoerced( |
| 10197 | .enum_type => { | 10192 | .enum_type => { |
| 10198 | const enum_type = ip.loadEnumType(new_ty); | 10193 | const enum_type = ip.loadEnumType(new_ty); |
| 10199 | const index = enum_type.nameIndex(ip, enum_literal).?; | 10194 | const index = enum_type.nameIndex(ip, enum_literal).?; |
| 10195 | assert(enum_type.int_tag_type != .noreturn_type); | ||
| 10200 | return ip.get(gpa, io, tid, .{ .enum_tag = .{ | 10196 | return ip.get(gpa, io, tid, .{ .enum_tag = .{ |
| 10201 | .ty = new_ty, | 10197 | .ty = new_ty, |
| 10202 | .int = if (enum_type.field_values.len != 0) | 10198 | .int = if (enum_type.field_values.len != 0) |
src/Sema.zig+248-99| ... | @@ -1191,11 +1191,14 @@ fn analyzeBodyInner( | ... | @@ -1191,11 +1191,14 @@ fn analyzeBodyInner( |
| 1191 | .elem_type => try sema.zirElemType(block, inst), | 1191 | .elem_type => try sema.zirElemType(block, inst), |
| 1192 | .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), | 1192 | .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), |
| 1193 | .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst), | 1193 | .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst), |
| 1194 | .from_backing_int_arg_ty => try sema.zirFromBackingIntArgTy(block, inst), | ||
| 1194 | .enum_literal => try sema.zirEnumLiteral(block, inst), | 1195 | .enum_literal => try sema.zirEnumLiteral(block, inst), |
| 1195 | .decl_literal => try sema.zirDeclLiteral(block, inst, true), | 1196 | .decl_literal => try sema.zirDeclLiteral(block, inst, true), |
| 1196 | .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false), | 1197 | .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false), |
| 1197 | .int_from_enum => try sema.zirIntFromEnum(block, inst), | 1198 | .int_from_enum => try sema.zirIntFromEnum(block, inst), |
| 1198 | .enum_from_int => try sema.zirEnumFromInt(block, inst), | 1199 | .enum_from_int => try sema.zirEnumFromInt(block, inst), |
| 1200 | .backing_int => try sema.zirBackingInt(block, inst), | ||
| 1201 | .from_backing_int => try sema.zirFromBackingInt(block, inst), | ||
| 1199 | .err_union_code => try sema.zirErrUnionCode(block, inst), | 1202 | .err_union_code => try sema.zirErrUnionCode(block, inst), |
| 1200 | .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), | 1203 | .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), |
| 1201 | .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), | 1204 | .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), |
| ... | @@ -2428,6 +2431,28 @@ fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src: | ... | @@ -2428,6 +2431,28 @@ fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src: |
| 2428 | }); | 2431 | }); |
| 2429 | } | 2432 | } |
| 2430 | 2433 | ||
| 2434 | fn 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 | |||
| 2431 | fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { | 2456 | fn 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 | } |
| 4784 | 4809 | ||
| 4785 | pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void { | 4810 | pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) Allocator.Error!void { |
| 4786 | const zcu = sema.pt.zcu; | 4811 | const zcu = sema.pt.zcu; |
| 4787 | const src_loc = decl_ty.srcLocOrNull(zcu) orelse return; | 4812 | const src_loc = decl_ty.srcLocOrNull(zcu) orelse return; |
| 4788 | const category = switch (decl_ty.zigTypeTag(zcu)) { | 4813 | const category = switch (decl_ty.zigTypeTag(zcu)) { |
| ... | @@ -7853,19 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -7853,19 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 7853 | }, | 7878 | }, |
| 7854 | }; | 7879 | }; |
| 7855 | const enum_tag_ty = sema.typeOf(enum_tag); | 7880 | const enum_tag_ty = sema.typeOf(enum_tag); |
| 7856 | const int_tag_ty = enum_tag_ty.intTagType(zcu); | 7881 | const int_tag_ty = enum_tag_ty.backingIntType(zcu); |
| 7857 | 7882 | assert(int_tag_ty.classify(zcu) != .no_possible_value); | |
| 7858 | // TODO: use correct solution | ||
| 7859 | // https://github.com/ziglang/zig/issues/15909 | ||
| 7860 | if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) { | ||
| 7861 | return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{ | ||
| 7862 | enum_tag_ty.fmt(pt), | ||
| 7863 | }); | ||
| 7864 | } | ||
| 7865 | 7883 | ||
| 7866 | if (sema.resolveValue(enum_tag)) |enum_tag_val| { | 7884 | if (sema.resolveValue(enum_tag)) |enum_tag_val| { |
| 7867 | if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty); | 7885 | if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty); |
| 7868 | return .fromValue(enum_tag_val.intFromEnum(zcu)); | 7886 | return .fromValue(enum_tag_val.backingInt(zcu)); |
| 7869 | } | 7887 | } |
| 7870 | 7888 | ||
| 7871 | try sema.requireRuntimeBlock(block, src, operand_src); | 7889 | try sema.requireRuntimeBlock(block, src, operand_src); |
| ... | @@ -7891,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -7891,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 7891 | 7909 | ||
| 7892 | if (sema.resolveValue(operand)) |int_val| { | 7910 | if (sema.resolveValue(operand)) |int_val| { |
| 7893 | if (dest_ty.isNonexhaustiveEnum(zcu)) { | 7911 | if (dest_ty.isNonexhaustiveEnum(zcu)) { |
| 7894 | const int_tag_ty = dest_ty.intTagType(zcu); | 7912 | const int_tag_ty = dest_ty.backingIntType(zcu); |
| 7895 | if (int_val.intFitsInType(int_tag_ty, null, zcu)) { | 7913 | if (int_val.intFitsInType(int_tag_ty, null, zcu)) { |
| 7896 | return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); | 7914 | return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); |
| 7897 | } | 7915 | } |
| ... | @@ -7910,15 +7928,11 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -7910,15 +7928,11 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 7910 | return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); | 7928 | return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); |
| 7911 | } | 7929 | } |
| 7912 | 7930 | ||
| 7913 | if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) { | ||
| 7914 | return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum }); | ||
| 7915 | } | ||
| 7916 | |||
| 7917 | if (try dest_ty.onePossibleValue(pt)) |opv| { | 7931 | if (try dest_ty.onePossibleValue(pt)) |opv| { |
| 7918 | if (block.wantSafety()) { | 7932 | if (block.wantSafety()) { |
| 7919 | // The operand is runtime-known but the result is comptime-known. In | 7933 | // The operand is runtime-known but the result is comptime-known. In |
| 7920 | // this case we still need a safety check. | 7934 | // this case we still need a safety check. |
| 7921 | const expect_int = try pt.getCoerced(opv.intFromEnum(zcu), operand_ty); | 7935 | const expect_int = try pt.getCoerced(opv.backingInt(zcu), operand_ty); |
| 7922 | const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int)); | 7936 | const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int)); |
| 7923 | try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); | 7937 | try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); |
| 7924 | } | 7938 | } |
| ... | @@ -9372,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -9372,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9372 | .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}), | 9386 | .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}), |
| 9373 | else => {}, | 9387 | else => {}, |
| 9374 | } | 9388 | } |
| 9375 | |||
| 9376 | break :msg msg; | 9389 | break :msg msg; |
| 9377 | }), | 9390 | }), |
| 9378 | .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) { | 9391 | .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) { |
| ... | @@ -9407,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -9407,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9407 | return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); | 9420 | return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); |
| 9408 | } | 9421 | } |
| 9409 | 9422 | ||
| 9410 | return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node)); | 9423 | operand_ty.assertHasLayout(zcu); |
| 9424 | try sema.ensureLayoutResolved(dest_ty, src, .init); | ||
| 9425 | |||
| 9426 | const operand_bits = operand_ty.bitSize(zcu); | ||
| 9427 | const dest_bits = dest_ty.bitSize(zcu); | ||
| 9428 | if (operand_bits != dest_bits) { | ||
| 9429 | return sema.fail(block, src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{ | ||
| 9430 | dest_ty.fmt(pt), | ||
| 9431 | dest_bits, | ||
| 9432 | operand_ty.fmt(pt), | ||
| 9433 | operand_bits, | ||
| 9434 | }); | ||
| 9435 | } | ||
| 9436 | |||
| 9437 | if (sema.resolveValue(operand)) |operand_val| { | ||
| 9438 | const dest_is_exhaustive_enum = dest_ty.zigTypeTag(zcu) == .@"enum" and | ||
| 9439 | !dest_ty.isNonexhaustiveEnum(zcu); | ||
| 9440 | if (dest_is_exhaustive_enum and operand_val.isUndef(zcu)) { | ||
| 9441 | return sema.failWithUseOfUndef(block, operand_src, null); | ||
| 9442 | } | ||
| 9443 | |||
| 9444 | const result_val = try sema.bitCastVal(operand_val, dest_ty); | ||
| 9445 | |||
| 9446 | if (dest_is_exhaustive_enum and | ||
| 9447 | dest_ty.enumTagFieldIndex(result_val, zcu) == null) | ||
| 9448 | { | ||
| 9449 | return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ | ||
| 9450 | dest_ty.fmt(pt), result_val.backingInt(zcu).fmtValueSema(pt, sema), | ||
| 9451 | }); | ||
| 9452 | } | ||
| 9453 | |||
| 9454 | return .fromValue(result_val); | ||
| 9455 | } | ||
| 9456 | |||
| 9457 | try sema.validateRuntimeValue(block, src, operand); | ||
| 9458 | |||
| 9459 | if (block.wantSafety()) { | ||
| 9460 | try sema.preparePanicId(src, .invalid_enum_value); | ||
| 9461 | return block.addTyOp(.bit_cast_safe, dest_ty, operand); | ||
| 9462 | } | ||
| 9463 | return block.addTyOp(.bit_cast, dest_ty, operand); | ||
| 9464 | } | ||
| 9465 | |||
| 9466 | fn 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 | |||
| 9524 | fn 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 | |||
| 9550 | fn zirFromBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 9551 | const pt = sema.pt; | ||
| 9552 | const zcu = pt.zcu; | ||
| 9553 | const ip = &zcu.intern_pool; | ||
| 9554 | |||
| 9555 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | ||
| 9556 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | ||
| 9557 | const src = block.nodeOffset(inst_data.src_node); | ||
| 9558 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); | ||
| 9559 | |||
| 9560 | // Type has already been validated and layout-resolved by `zirFromBackingIntArgTy`. | ||
| 9561 | const dest_ty = try sema.resolveDestType(block, .unneeded, extra.lhs, .remove_eu_opt, undefined); | ||
| 9562 | dest_ty.assertHasLayout(zcu); | ||
| 9563 | |||
| 9564 | const operand = sema.resolveInst(extra.rhs); | ||
| 9565 | const backing_int_ref = try sema.coerce(block, dest_ty.backingIntType(zcu), operand, operand_src); | ||
| 9566 | |||
| 9567 | if (sema.resolveValue(backing_int_ref)) |backing_int_val| { | ||
| 9568 | if (dest_ty.zigTypeTag(zcu) != .@"enum") { | ||
| 9569 | // we don't do any safety checks for bitpacks | ||
| 9570 | if (backing_int_val.isUndef(zcu)) return pt.undefRef(dest_ty); | ||
| 9571 | return .fromValue(try pt.bitpackValue(dest_ty, backing_int_val)); | ||
| 9572 | } | ||
| 9573 | const enum_obj = ip.loadEnumType(dest_ty.toIntern()); | ||
| 9574 | if (backing_int_val.isUndef(zcu)) { | ||
| 9575 | if (enum_obj.nonexhaustive) return pt.undefRef(dest_ty); | ||
| 9576 | return sema.failWithUseOfUndef(block, operand_src, null); | ||
| 9577 | } | ||
| 9578 | if (!enum_obj.nonexhaustive and | ||
| 9579 | enum_obj.tagValueIndex(ip, backing_int_val.toIntern()) == null) | ||
| 9580 | { | ||
| 9581 | return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ | ||
| 9582 | dest_ty.fmt(pt), backing_int_val.fmtValueSema(pt, sema), | ||
| 9583 | }); | ||
| 9584 | } | ||
| 9585 | return .fromValue(try pt.enumValue(dest_ty, backing_int_val)); | ||
| 9586 | } | ||
| 9587 | |||
| 9588 | switch (dest_ty.classify(zcu)) { | ||
| 9589 | .partially_comptime, .fully_comptime => unreachable, // does not apply to enums or bitpacks | ||
| 9590 | .no_possible_value => unreachable, // backing int also NPV, cannot coerce to NPV type | ||
| 9591 | .one_possible_value => unreachable, // backing int also OPV, should have been resolve above | ||
| 9592 | .runtime => {}, | ||
| 9593 | } | ||
| 9594 | |||
| 9595 | if (block.wantSafety()) { | ||
| 9596 | try sema.preparePanicId(src, .invalid_enum_value); | ||
| 9597 | return block.addTyOp(.bit_cast_safe, dest_ty, backing_int_ref); | ||
| 9598 | } | ||
| 9599 | return block.addTyOp(.bit_cast, dest_ty, backing_int_ref); | ||
| 9411 | } | 9600 | } |
| 9412 | 9601 | ||
| 9413 | fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9602 | fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -9940,12 +10129,8 @@ fn analyzeSwitchBlock( | ... | @@ -9940,12 +10129,8 @@ fn analyzeSwitchBlock( |
| 9940 | operand_ty.containerLayout(zcu) != .@"packed"; | 10129 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 9941 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; | 10130 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 9942 | 10131 | ||
| 9943 | if (item_ty.zigTypeTag(zcu) == .@"enum" and | 10132 | // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909) |
| 9944 | validated_switch.seen_enum_fields.len == 0 and | 10133 | if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated |
| 9945 | !operand_ty.isNonexhaustiveEnum(zcu)) | ||
| 9946 | { | ||
| 9947 | return .void_value; // switch on empty enum/union | ||
| 9948 | } | ||
| 9949 | 10134 | ||
| 9950 | const cond_ref = switch (operand) { | 10135 | const cond_ref = switch (operand) { |
| 9951 | .simple => |s| s.cond, | 10136 | .simple => |s| s.cond, |
| ... | @@ -10451,26 +10636,11 @@ fn finishSwitchBr( | ... | @@ -10451,26 +10636,11 @@ fn finishSwitchBr( |
| 10451 | } | 10636 | } |
| 10452 | 10637 | ||
| 10453 | var prev_result_overflowed = false; | 10638 | var prev_result_overflowed = false; |
| 10454 | while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({ | 10639 | while (item.compareScalar(.lte, item_last, item_ty, zcu)) : ({ |
| 10455 | const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { | ||
| 10456 | .int => .{ item, operand_ty }, | ||
| 10457 | .@"enum" => b: { | ||
| 10458 | const int_val: Value = .fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int); | ||
| 10459 | break :b .{ int_val, int_val.typeOf(zcu) }; | ||
| 10460 | }, | ||
| 10461 | else => unreachable, | ||
| 10462 | }; | ||
| 10463 | assert(!prev_result_overflowed); | 10640 | assert(!prev_result_overflowed); |
| 10464 | const result = try arith.incrementDefinedInt(sema, int_ty, int_val); | 10641 | const result = try arith.incrementDefinedInt(sema, item_ty, item); |
| 10465 | prev_result_overflowed = result.overflow; | 10642 | prev_result_overflowed = result.overflow; |
| 10466 | item = switch (operand_ty.zigTypeTag(zcu)) { | 10643 | item = result.val; |
| 10467 | .int => result.val, | ||
| 10468 | .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{ | ||
| 10469 | .ty = operand_ty.toIntern(), | ||
| 10470 | .int = result.val.toIntern(), | ||
| 10471 | } })), | ||
| 10472 | else => unreachable, | ||
| 10473 | }; | ||
| 10474 | }) { | 10644 | }) { |
| 10475 | cases_len += 1; | 10645 | cases_len += 1; |
| 10476 | case_block.instructions.clearRetainingCapacity(); | 10646 | case_block.instructions.clearRetainingCapacity(); |
| ... | @@ -10603,7 +10773,7 @@ fn finishSwitchBr( | ... | @@ -10603,7 +10773,7 @@ fn finishSwitchBr( |
| 10603 | break :check_enumerable .{ undefined, min_int }; | 10773 | break :check_enumerable .{ undefined, min_int }; |
| 10604 | }, | 10774 | }, |
| 10605 | .@"union", .@"struct" => { | 10775 | .@"union", .@"struct" => { |
| 10606 | const backing_int_ty = item_ty.bitpackBackingInt(zcu); | 10776 | const backing_int_ty = item_ty.backingIntType(zcu); |
| 10607 | const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty); | 10777 | const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty); |
| 10608 | break :check_enumerable .{ undefined, min_backing_int }; | 10778 | break :check_enumerable .{ undefined, min_backing_int }; |
| 10609 | }, | 10779 | }, |
| ... | @@ -10904,7 +11074,7 @@ const ValidatedSwitchBlock = struct { | ... | @@ -10904,7 +11074,7 @@ const ValidatedSwitchBlock = struct { |
| 10904 | var cur_val = it.next_val orelse return null; | 11074 | var cur_val = it.next_val orelse return null; |
| 10905 | const int_ty = switch (type_tag) { | 11075 | const int_ty = switch (type_tag) { |
| 10906 | .int => item_ty, | 11076 | .int => item_ty, |
| 10907 | .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu), | 11077 | .@"union", .@"struct" => item_ty.backingIntType(zcu), |
| 10908 | else => unreachable, | 11078 | else => unreachable, |
| 10909 | }; | 11079 | }; |
| 10910 | while (it.next_idx < it.seen_ranges.len and | 11080 | while (it.next_idx < it.seen_ranges.len and |
| ... | @@ -11334,7 +11504,7 @@ fn validateSwitchBlock( | ... | @@ -11334,7 +11504,7 @@ fn validateSwitchBlock( |
| 11334 | check_range: { | 11504 | check_range: { |
| 11335 | const int_ty = switch (type_tag) { | 11505 | const int_ty = switch (type_tag) { |
| 11336 | .int => item_ty, | 11506 | .int => item_ty, |
| 11337 | .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu), | 11507 | .@"union", .@"struct" => item_ty.backingIntType(zcu), |
| 11338 | else => unreachable, | 11508 | else => unreachable, |
| 11339 | }; | 11509 | }; |
| 11340 | const min_int = try int_ty.minInt(pt, int_ty); | 11510 | const min_int = try int_ty.minInt(pt, int_ty); |
| ... | @@ -11956,14 +12126,7 @@ fn analyzeSwitchCaptures( | ... | @@ -11956,14 +12126,7 @@ fn analyzeSwitchCaptures( |
| 11956 | try sema.analyzeUnreachable(case_block, operand_src, false); | 12126 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 11957 | break :payload_ref .unreachable_value; | 12127 | break :payload_ref .unreachable_value; |
| 11958 | }; | 12128 | }; |
| 11959 | if (sema.resolveValue(loaded_operand)) |err_val| { | 12129 | break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand); |
| 11960 | break :payload_ref .fromIntern(try pt.intern(.{ .err = .{ | ||
| 11961 | .ty = capture_err_ty.toIntern(), | ||
| 11962 | .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name, | ||
| 11963 | } })); | ||
| 11964 | } else { | ||
| 11965 | break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand); | ||
| 11966 | } | ||
| 11967 | }, | 12130 | }, |
| 11968 | .item_refs => |item_refs| { | 12131 | .item_refs => |item_refs| { |
| 11969 | var names: InferredErrorSet.NameMap = .{}; | 12132 | var names: InferredErrorSet.NameMap = .{}; |
| ... | @@ -11973,14 +12136,7 @@ fn analyzeSwitchCaptures( | ... | @@ -11973,14 +12136,7 @@ fn analyzeSwitchCaptures( |
| 11973 | names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {}); | 12136 | names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {}); |
| 11974 | } | 12137 | } |
| 11975 | const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys()); | 12138 | const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 11976 | if (sema.resolveValue(loaded_operand)) |err_val| { | 12139 | break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand); |
| 11977 | break :payload_ref .fromIntern(try pt.intern(.{ .err = .{ | ||
| 11978 | .ty = capture_err_ty.toIntern(), | ||
| 11979 | .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name, | ||
| 11980 | } })); | ||
| 11981 | } else { | ||
| 11982 | break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand); | ||
| 11983 | } | ||
| 11984 | }, | 12140 | }, |
| 11985 | } | 12141 | } |
| 11986 | } | 12142 | } |
| ... | @@ -12461,7 +12617,7 @@ fn validateSwitchItemOrRange( | ... | @@ -12461,7 +12617,7 @@ fn validateSwitchItemOrRange( |
| 12461 | .first = .fromInterned(backing_int_val), | 12617 | .first = .fromInterned(backing_int_val), |
| 12462 | .last = .fromInterned(backing_int_val), | 12618 | .last = .fromInterned(backing_int_val), |
| 12463 | .src = item_src, | 12619 | .src = item_src, |
| 12464 | }, item_ty.bitpackBackingInt(zcu), zcu); | 12620 | }, item_ty.backingIntType(zcu), zcu); |
| 12465 | }, | 12621 | }, |
| 12466 | .enum_literal, .@"fn", .pointer, .type => { | 12622 | .enum_literal, .@"fn", .pointer, .type => { |
| 12467 | break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| | 12623 | break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| |
| ... | @@ -18418,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -18418,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 18418 | const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src); | 18574 | const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src); |
| 18419 | 18575 | ||
| 18420 | if (union_ty.containerLayout(zcu) == .@"packed") { | 18576 | if (union_ty.containerLayout(zcu) == .@"packed") { |
| 18421 | return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node)); | 18577 | return sema.bitCastUnchecked(block, union_ty, payload); |
| 18422 | } | 18578 | } |
| 18423 | 18579 | ||
| 18424 | if (sema.resolveValue(payload)) |payload_val| { | 18580 | if (sema.resolveValue(payload)) |payload_val| { |
| ... | @@ -18555,7 +18711,7 @@ fn zirStructInit( | ... | @@ -18555,7 +18711,7 @@ fn zirStructInit( |
| 18555 | const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); | 18711 | const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); |
| 18556 | 18712 | ||
| 18557 | if (resolved_ty.containerLayout(zcu) == .@"packed") { | 18713 | if (resolved_ty.containerLayout(zcu) == .@"packed") { |
| 18558 | const union_val = try sema.bitCast(block, resolved_ty, init_inst, src); | 18714 | const union_val = try sema.bitCastUnchecked(block, resolved_ty, init_inst); |
| 18559 | const result_val = try sema.coerce(block, result_ty, union_val, src); | 18715 | const result_val = try sema.coerce(block, result_ty, union_val, src); |
| 18560 | if (is_ref) { | 18716 | if (is_ref) { |
| 18561 | return sema.analyzeRef(block, src, result_val, .none); | 18717 | return sema.analyzeRef(block, src, result_val, .none); |
| ... | @@ -19567,14 +19723,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -19567,14 +19723,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19567 | operand_ty.fmt(pt), | 19723 | operand_ty.fmt(pt), |
| 19568 | }), | 19724 | }), |
| 19569 | }; | 19725 | }; |
| 19570 | if (enum_ty.enumFieldCount(zcu) == 0) { | ||
| 19571 | // TODO I don't think this is the correct way to handle this but | ||
| 19572 | // it prevents a crash. | ||
| 19573 | // https://github.com/ziglang/zig/issues/15909 | ||
| 19574 | return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{ | ||
| 19575 | enum_ty.fmt(pt), | ||
| 19576 | }); | ||
| 19577 | } | ||
| 19578 | const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); | 19726 | const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); |
| 19579 | if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { | 19727 | if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { |
| 19580 | const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse { | 19728 | const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse { |
| ... | @@ -29742,41 +29890,29 @@ fn storePtrVal( | ... | @@ -29742,41 +29890,29 @@ fn storePtrVal( |
| 29742 | } | 29890 | } |
| 29743 | } | 29891 | } |
| 29744 | 29892 | ||
| 29745 | fn bitCast( | 29893 | /// Asserts that the layout of `dest_ty` is already resolved. |
| 29894 | fn bitCastUnchecked( | ||
| 29746 | sema: *Sema, | 29895 | sema: *Sema, |
| 29747 | block: *Block, | 29896 | block: *Block, |
| 29748 | dest_ty: Type, | 29897 | dest_ty: Type, |
| 29749 | inst: Air.Inst.Ref, | 29898 | inst: Air.Inst.Ref, |
| 29750 | inst_src: LazySrcLoc, | ||
| 29751 | ) CompileError!Air.Inst.Ref { | 29899 | ) CompileError!Air.Inst.Ref { |
| 29752 | const pt = sema.pt; | 29900 | const zcu = sema.pt.zcu; |
| 29753 | const zcu = pt.zcu; | ||
| 29754 | const old_ty = sema.typeOf(inst); | 29901 | const old_ty = sema.typeOf(inst); |
| 29755 | 29902 | ||
| 29756 | old_ty.assertHasLayout(zcu); | 29903 | old_ty.assertHasLayout(zcu); |
| 29757 | try sema.ensureLayoutResolved(dest_ty, inst_src, .init); | 29904 | dest_ty.assertHasLayout(zcu); |
| 29758 | 29905 | ||
| 29759 | assert(old_ty.hasBitRepresentation(zcu)); | 29906 | assert(old_ty.hasBitRepresentation(zcu)); |
| 29760 | assert(dest_ty.hasBitRepresentation(zcu)); | 29907 | assert(dest_ty.hasBitRepresentation(zcu)); |
| 29761 | assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer); | 29908 | assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer); |
| 29762 | assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer); | 29909 | assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer); |
| 29763 | 29910 | assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu)); | |
| 29764 | const dest_bits = dest_ty.bitSize(zcu); | ||
| 29765 | const old_bits = old_ty.bitSize(zcu); | ||
| 29766 | |||
| 29767 | if (old_bits != dest_bits) { | ||
| 29768 | return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{ | ||
| 29769 | dest_ty.fmt(pt), | ||
| 29770 | dest_bits, | ||
| 29771 | old_ty.fmt(pt), | ||
| 29772 | old_bits, | ||
| 29773 | }); | ||
| 29774 | } | ||
| 29775 | 29911 | ||
| 29776 | if (sema.resolveValue(inst)) |val| { | 29912 | if (sema.resolveValue(inst)) |val| { |
| 29777 | return .fromValue(try sema.bitCastVal(val, dest_ty)); | 29913 | return .fromValue(try sema.bitCastVal(val, dest_ty)); |
| 29778 | } | 29914 | } |
| 29779 | try sema.validateRuntimeValue(block, inst_src, inst); | 29915 | |
| 29780 | return block.addTyOp(.bit_cast, dest_ty, inst); | 29916 | return block.addTyOp(.bit_cast, dest_ty, inst); |
| 29781 | } | 29917 | } |
| 29782 | 29918 | ||
| ... | @@ -29800,6 +29936,26 @@ pub fn bitCastVal( | ... | @@ -29800,6 +29936,26 @@ pub fn bitCastVal( |
| 29800 | } | 29936 | } |
| 29801 | } | 29937 | } |
| 29802 | 29938 | ||
| 29939 | fn 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 | |||
| 29803 | fn checkSpirvSliceAllowed( | 29959 | fn checkSpirvSliceAllowed( |
| 29804 | sema: *Sema, | 29960 | sema: *Sema, |
| 29805 | block: *Block, | 29961 | block: *Block, |
| ... | @@ -33968,14 +34124,6 @@ fn intFromFloatScalar( | ... | @@ -33968,14 +34124,6 @@ fn intFromFloatScalar( |
| 33968 | return pt.getCoerced(cti_result, int_ty); | 34124 | return pt.getCoerced(cti_result, int_ty); |
| 33969 | } | 34125 | } |
| 33970 | 34126 | ||
| 33971 | fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { | ||
| 33972 | const pt = sema.pt; | ||
| 33973 | if (!int_val.compareAllWithZero(.gte, pt.zcu)) return false; | ||
| 33974 | const end_val = try pt.intValue(tag_ty, end); | ||
| 33975 | if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; | ||
| 33976 | return true; | ||
| 33977 | } | ||
| 33978 | |||
| 33979 | /// Asserts the type is an exhaustive enum. | 34127 | /// Asserts the type is an exhaustive enum. |
| 33980 | fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { | 34128 | fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { |
| 33981 | const pt = sema.pt; | 34129 | const pt = sema.pt; |
| ... | @@ -33985,6 +34133,7 @@ fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { | ... | @@ -33985,6 +34133,7 @@ fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { |
| 33985 | // The `tagValueIndex` function call below relies on the type being the integer tag type. | 34133 | // The `tagValueIndex` function call below relies on the type being the integer tag type. |
| 33986 | // `getCoerced` assumes the value will fit the new type. | 34134 | // `getCoerced` assumes the value will fit the new type. |
| 33987 | const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type); | 34135 | const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type); |
| 34136 | if (int_tag_ty.classify(zcu) == .no_possible_value) return false; | ||
| 33988 | if (!int.intFitsInType(int_tag_ty, null, zcu)) return false; | 34137 | if (!int.intFitsInType(int_tag_ty, null, zcu)) return false; |
| 33989 | const int_coerced = try pt.getCoerced(int, int_tag_ty); | 34138 | const int_coerced = try pt.getCoerced(int, int_tag_ty); |
| 33990 | return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; | 34139 | return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; |
src/Sema/reinterpret.zig+4-4| ... | @@ -353,7 +353,7 @@ const PackValueBytes = struct { | ... | @@ -353,7 +353,7 @@ const PackValueBytes = struct { |
| 353 | return pt.aggregateValue(ty, elems); | 353 | return pt.aggregateValue(ty, elems); |
| 354 | }, | 354 | }, |
| 355 | .@"packed" => { | 355 | .@"packed" => { |
| 356 | const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu)); | 356 | const backing_int_val = try pack.primitive(ty.backingIntType(zcu)); |
| 357 | if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty); | 357 | if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty); |
| 358 | return pt.bitpackValue(ty, backing_int_val); | 358 | return pt.bitpackValue(ty, backing_int_val); |
| 359 | }, | 359 | }, |
| ... | @@ -424,15 +424,15 @@ const PackValueBytes = struct { | ... | @@ -424,15 +424,15 @@ const PackValueBytes = struct { |
| 424 | } | 424 | } |
| 425 | }, | 425 | }, |
| 426 | .@"packed" => { | 426 | .@"packed" => { |
| 427 | const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu)); | 427 | const backing_int_val = try pack.primitive(ty.backingIntType(zcu)); |
| 428 | if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty); | 428 | if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty); |
| 429 | return pt.bitpackValue(ty, backing_int_val); | 429 | return pt.bitpackValue(ty, backing_int_val); |
| 430 | }, | 430 | }, |
| 431 | }, | 431 | }, |
| 432 | .@"enum" => { | 432 | .@"enum" => { |
| 433 | const tag_int_val = try pack.primitive(ty.intTagType(zcu)); | 433 | const tag_int_val = try pack.primitive(ty.backingIntType(zcu)); |
| 434 | if (tag_int_val.isUndef(zcu)) return pt.undefValue(ty); | 434 | if (tag_int_val.isUndef(zcu)) return pt.undefValue(ty); |
| 435 | return pt.enumValue(ty, tag_int_val.toIntern()); | 435 | return pt.enumValue(ty, tag_int_val); |
| 436 | }, | 436 | }, |
| 437 | else => return pack.primitive(ty), | 437 | else => return pack.primitive(ty), |
| 438 | } | 438 | } |
src/Sema/type_resolution.zig+23-7| ... | @@ -63,7 +63,7 @@ pub const LayoutResolveReason = enum { | ... | @@ -63,7 +63,7 @@ pub const LayoutResolveReason = enum { |
| 63 | .@"export" => "for export here", | 63 | .@"export" => "for export here", |
| 64 | .@"extern" => "for extern declaration here", | 64 | .@"extern" => "for extern declaration here", |
| 65 | .asm_out_type => "for inline assembly output type declared here", | 65 | .asm_out_type => "for inline assembly output type declared here", |
| 66 | .std_lang_type => "from 'std.lang'", | 66 | .std_lang_type => "from 'std.lang'", |
| 67 | // zig fmt: on | 67 | // zig fmt: on |
| 68 | }; | 68 | }; |
| 69 | } | 69 | } |
| ... | @@ -1325,15 +1325,31 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { | ... | @@ -1325,15 +1325,31 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { |
| 1325 | const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); | 1325 | const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); |
| 1326 | break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref); | 1326 | break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref); |
| 1327 | }; | 1327 | }; |
| 1328 | const empty_exhaustive = enum_obj.field_names.len == 0 and !enum_obj.nonexhaustive; | ||
| 1328 | const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: { | 1329 | const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: { |
| 1329 | if (int_tag_ty.zigTypeTag(zcu) != .int) return sema.fail( | 1330 | switch (int_tag_ty.zigTypeTag(zcu)) { |
| 1330 | &block, | 1331 | .int => if (empty_exhaustive) return sema.fail( |
| 1331 | block.src(.container_arg), | 1332 | &block, |
| 1332 | "expected integer tag type, found '{f}'", | 1333 | block.src(.container_arg), |
| 1333 | .{int_tag_ty.fmt(pt)}, | 1334 | "empty exhaustive enums must be backed by 'noreturn'", |
| 1334 | ); | 1335 | .{}, |
| 1336 | ), | ||
| 1337 | .noreturn => if (!empty_exhaustive) return sema.fail( | ||
| 1338 | &block, | ||
| 1339 | block.src(.container_arg), | ||
| 1340 | "non-empty enums cannot be backed by 'noreturn'", | ||
| 1341 | .{}, | ||
| 1342 | ), | ||
| 1343 | else => return sema.fail( | ||
| 1344 | &block, | ||
| 1345 | block.src(.container_arg), | ||
| 1346 | "expected integer tag type, found '{f}'", | ||
| 1347 | .{int_tag_ty.fmt(pt)}, | ||
| 1348 | ), | ||
| 1349 | } | ||
| 1335 | break :ty int_tag_ty; | 1350 | break :ty int_tag_ty; |
| 1336 | } else ty: { | 1351 | } else ty: { |
| 1352 | if (empty_exhaustive) break :ty .noreturn; | ||
| 1337 | // Infer the int tag type from the field count | 1353 | // Infer the int tag type from the field count |
| 1338 | const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1); | 1354 | const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1); |
| 1339 | break :ty try pt.intType(.unsigned, bits); | 1355 | break :ty try pt.intType(.unsigned, bits); |
src/Type.zig+26-20| ... | @@ -1580,15 +1580,28 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.lang.Type.ContainerLayout | ... | @@ -1580,15 +1580,28 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.lang.Type.ContainerLayout |
| 1580 | }; | 1580 | }; |
| 1581 | } | 1581 | } |
| 1582 | 1582 | ||
| 1583 | pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type { | 1583 | /// Asserts that the type is either an enum or a bitpack. |
| 1584 | pub 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 | } |
| 1591 | 1593 | ||
| 1594 | /// For unions, returns the *backing int* mode, not the *enum tag* mode. | ||
| 1595 | pub 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. |
| 1593 | pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type { | 1606 | pub 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, |
| 2102 | 2112 | ||
| 2103 | // values, not types | 2113 | // 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 | } |
| 2276 | 2286 | ||
| 2277 | /// Asserts the type is an enum or a union. | ||
| 2278 | pub 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 | |||
| 2288 | pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool { | 2287 | pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool { |
| 2289 | const ip = &zcu.intern_pool; | 2288 | const ip = &zcu.intern_pool; |
| 2290 | return switch (ip.indexToKey(ty.toIntern())) { | 2289 | return switch (ip.indexToKey(ty.toIntern())) { |
| ... | @@ -3058,8 +3057,11 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason { | ... | @@ -3058,8 +3057,11 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason { |
| 3058 | .one, .many, .c => .pointer, | 3057 | .one, .many, .c => .pointer, |
| 3059 | }, | 3058 | }, |
| 3060 | 3059 | ||
| 3061 | .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode) { | 3060 | .@"enum" => switch (ty.backingIntMode(zcu)) { |
| 3062 | .explicit => null, | 3061 | .explicit => switch (ty.backingIntType(zcu).toIntern()) { |
| 3062 | else => null, | ||
| 3063 | .noreturn_type => .other, | ||
| 3064 | }, | ||
| 3063 | .auto => .{ .enum_inferred_int_tag = ty }, | 3065 | .auto => .{ .enum_inferred_int_tag = ty }, |
| 3064 | }, | 3066 | }, |
| 3065 | 3067 | ||
| ... | @@ -3225,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool { | ... | @@ -3225,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool { |
| 3225 | .float, | 3227 | .float, |
| 3226 | => true, | 3228 | => true, |
| 3227 | 3229 | ||
| 3228 | .@"enum" => zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode == .explicit, | 3230 | .@"enum" => { |
| 3231 | const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern()); | ||
| 3232 | return enum_obj.int_tag_mode == .explicit and | ||
| 3233 | enum_obj.int_tag_type != .noreturn_type; | ||
| 3234 | }, | ||
| 3229 | .pointer, .optional => ty.isPtrAtRuntime(zcu), | 3235 | .pointer, .optional => ty.isPtrAtRuntime(zcu), |
| 3230 | .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed", | 3236 | .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed", |
| 3231 | 3237 |
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 | } |
| 148 | 148 | ||
| 149 | pub 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); | 150 | pub 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 | } |
| 152 | 157 | ||
| 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), |
| 2369 | 2374 | ||
| 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 | } |
| 3957 | 3957 | ||
| 3958 | pub fn intType(pt: Zcu.PerThread, signedness: std.lang.Signedness, bits: u16) Allocator.Error!Type { | 3958 | pub 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 | } |
| 3968 | 3968 | ||
| 3969 | pub fn arrayType(pt: Zcu.PerThread, info: InternPool.Key.ArrayType) Allocator.Error!Type { | 3969 | pub 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 | } |
| 3972 | 3972 | ||
| 3973 | pub fn vectorType(pt: Zcu.PerThread, info: InternPool.Key.VectorType) Allocator.Error!Type { | 3973 | pub 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 | } |
| 3976 | 3976 | ||
| 3977 | pub fn optionalType(pt: Zcu.PerThread, child_type: InternPool.Index) Allocator.Error!Type { | 3977 | pub 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 | } |
| 3980 | 3980 | ||
| 3981 | pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!Type { | 3981 | pub 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 | } |
| 3999 | 3999 | ||
| 4000 | return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info })); | 4000 | return .fromInterned(try pt.intern(.{ .ptr_type = canon_info })); |
| 4001 | } | 4001 | } |
| 4002 | 4002 | ||
| 4003 | pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type { | 4003 | pub 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. |
| 4039 | pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type { | 4039 | pub 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 | } |
| 4042 | 4042 | ||
| 4043 | pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type { | 4043 | pub 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 |
| 4050 | pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type { | 4050 | pub 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 | } |
| 4055 | 4055 | ||
| 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 | } |
| 4071 | 4071 | ||
| 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 | } |
| 4083 | 4083 | ||
| 4084 | /// Creates an enum tag value based on the integer tag value. | 4084 | /// Creates an enum tag value based on the integer tag value. |
| 4085 | pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value { | 4085 | pub 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 | } |
| 4095 | 4092 | ||
| ... | @@ -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 |
| 4103 | 4100 | ||
| 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 |
| 4262 | 4259 | ||
| 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. |
| 4264 | pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value { | 4261 | pub 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"); |
| 5 | pub const Mir = @import("aarch64/Mir.zig"); | 5 | pub const Mir = @import("aarch64/Mir.zig"); |
| 6 | pub const Select = @import("aarch64/Select.zig"); | 6 | pub const Select = @import("aarch64/Select.zig"); |
| 7 | 7 | ||
| 8 | pub fn legalizeFeatures(_: *const std.Target) ?*Air.Legalize.Features { | 8 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 9 | return null; | 9 | return comptime &.initMany(&.{ |
| 10 | .expand_bit_cast_safe, | ||
| 11 | }); | ||
| 10 | } | 12 | } |
| 11 | 13 | ||
| 12 | pub fn generate( | 14 | pub 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 modes | 29 | // 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 bits | 3254 | .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 types | 3258 | // 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 | } |
| 4541 | 4541 | ||
| 4542 | fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | 4542 | fn 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; |
| 4545 | 4545 | ||
| ... | @@ -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)); |
| 4565 | 4565 | ||
| 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 | } |
| 4569 | 4588 | ||
| 4570 | fn airNopCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | 4589 | fn 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}; |
| 51 | 51 | ||
| 52 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | 52 | pub 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 | }; |
| 5149 | 5150 |
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 { |
| 1374 | 1375 | ||
| 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 { |
| 121 | 121 | ||
| 122 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | 122 | pub 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( |
| 6887 | 6888 | ||
| 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; |
| 6893 | 6894 | ||
| ... | @@ -8164,7 +8165,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { | ... | @@ -8164,7 +8165,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 8164 | break :blk if (backing_bits <= 32) 1 else 2; | 8165 | break :blk if (backing_bits <= 32) 1 else 2; |
| 8165 | }, | 8166 | }, |
| 8166 | .@"enum" => blk: { | 8167 | .@"enum" => blk: { |
| 8167 | const int_ty = cond_ty.intTagType(zcu); | 8168 | const int_ty = cond_ty.backingIntType(zcu); |
| 8168 | const int_info = int_ty.intInfo(zcu); | 8169 | const int_info = int_ty.intInfo(zcu); |
| 8169 | const backing_bits, const big_int = cg.backingIntBits(int_info.bits); | 8170 | const backing_bits, const big_int = cg.backingIntBits(int_info.bits); |
| 8170 | if (big_int) return cg.todo("implement composite int switch", .{}); | 8171 | if (big_int) return cg.todo("implement composite int switch", .{}); |
| ... | @@ -8224,7 +8225,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { | ... | @@ -8224,7 +8225,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 8224 | const value: Value = .fromInterned(item.toInterned().?); | 8225 | const value: Value = .fromInterned(item.toInterned().?); |
| 8225 | const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { | 8226 | const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { |
| 8226 | .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), | 8227 | .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), |
| 8227 | .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu), | 8228 | .@"enum" => value.backingInt(zcu).toUnsignedInt(zcu), |
| 8228 | .error_set => value.getErrorInt(zcu), | 8229 | .error_set => value.getErrorInt(zcu), |
| 8229 | .pointer => value.toUnsignedInt(zcu), | 8230 | .pointer => value.toUnsignedInt(zcu), |
| 8230 | else => unreachable, | 8231 | else => unreachable, |
| ... | @@ -8378,7 +8379,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { | ... | @@ -8378,7 +8379,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 8378 | break :blk if (backing_bits <= 32) 1 else 2; | 8379 | break :blk if (backing_bits <= 32) 1 else 2; |
| 8379 | }, | 8380 | }, |
| 8380 | .@"enum" => blk: { | 8381 | .@"enum" => blk: { |
| 8381 | const int_ty = cond_ty.intTagType(zcu); | 8382 | const int_ty = cond_ty.backingIntType(zcu); |
| 8382 | const int_info = int_ty.intInfo(zcu); | 8383 | const int_info = int_ty.intInfo(zcu); |
| 8383 | const backing_bits, const big_int = cg.backingIntBits(int_info.bits); | 8384 | const backing_bits, const big_int = cg.backingIntBits(int_info.bits); |
| 8384 | if (big_int) return cg.todo("implement composite int loop switch", .{}); | 8385 | if (big_int) return cg.todo("implement composite int loop switch", .{}); |
| ... | @@ -8464,7 +8465,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { | ... | @@ -8464,7 +8465,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 8464 | const value: Value = .fromInterned(item.toInterned().?); | 8465 | const value: Value = .fromInterned(item.toInterned().?); |
| 8465 | const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { | 8466 | const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { |
| 8466 | .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), | 8467 | .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), |
| 8467 | .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu), | 8468 | .@"enum" => value.backingInt(zcu).toUnsignedInt(zcu), |
| 8468 | .error_set => value.getErrorInt(zcu), | 8469 | .error_set => value.getErrorInt(zcu), |
| 8469 | .pointer => value.toUnsignedInt(zcu), | 8470 | .pointer => value.toUnsignedInt(zcu), |
| 8470 | else => unreachable, | 8471 | else => unreachable, |
src/codegen/wasm/CodeGen.zig+9-15| ... | @@ -32,6 +32,7 @@ const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; | ... | @@ -32,6 +32,7 @@ const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; |
| 32 | 32 | ||
| 33 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | 33 | pub 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/immediate | 622 | else => .i32, // all represented as reference/immediate |
| ... | @@ -1226,7 +1227,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { | ... | @@ -1226,7 +1227,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { |
| 1226 | .frame, | 1227 | .frame, |
| 1227 | => return ty.hasRuntimeBits(zcu), | 1228 | => return ty.hasRuntimeBits(zcu), |
| 1228 | .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { | 1229 | .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { |
| 1229 | .@"packed" => return isByRef(ty.bitpackBackingInt(zcu), zcu, target), | 1230 | .@"packed" => return isByRef(ty.backingIntType(zcu), zcu, target), |
| 1230 | .@"extern", .auto => return ty.hasRuntimeBits(zcu), | 1231 | .@"extern", .auto => return ty.hasRuntimeBits(zcu), |
| 1231 | }, | 1232 | }, |
| 1232 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, | 1233 | .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, |
| ... | @@ -1905,6 +1906,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1905,6 +1906,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1905 | .add_safe, | 1906 | .add_safe, |
| 1906 | .sub_safe, | 1907 | .sub_safe, |
| 1907 | .mul_safe, | 1908 | .mul_safe, |
| 1909 | .bit_cast_safe, | ||
| 1908 | .int_cast_safe, | 1910 | .int_cast_safe, |
| 1909 | .int_from_float_safe, | 1911 | .int_from_float_safe, |
| 1910 | .int_from_float_optimized_safe, | 1912 | .int_from_float_optimized_safe, |
| ... | @@ -2525,18 +2527,10 @@ const IntType = struct { | ... | @@ -2525,18 +2527,10 @@ const IntType = struct { |
| 2525 | .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable, | 2527 | .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable, |
| 2526 | .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable, | 2528 | .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable, |
| 2527 | }, | 2529 | }, |
| 2528 | .struct_type => { | 2530 | .enum_type, |
| 2529 | const loaded_struct = ip.loadStructType(ty_index); | 2531 | .struct_type, |
| 2530 | switch (loaded_struct.layout) { | 2532 | .union_type, |
| 2531 | .auto, .@"extern" => unreachable, | 2533 | => ty_index = Type.fromInterned(ty_index).backingIntType(zcu).toIntern(), |
| 2532 | .@"packed" => ty_index = loaded_struct.packed_backing_int_type, | ||
| 2533 | } | ||
| 2534 | }, | ||
| 2535 | .union_type => return switch (ip.loadUnionType(ty_index).layout) { | ||
| 2536 | .auto, .@"extern" => unreachable, | ||
| 2537 | .@"packed" => .{ .is_signed = false, .bits = @intCast(ty.bitSize(zcu)) }, | ||
| 2538 | }, | ||
| 2539 | .enum_type => ty_index = ip.loadEnumType(ty_index).int_tag_type, | ||
| 2540 | .error_set_type, .inferred_error_set_type => return .{ .is_signed = false, .bits = zcu.errorSetBits() }, | 2534 | .error_set_type, .inferred_error_set_type => return .{ .is_signed = false, .bits = zcu.errorSetBits() }, |
| 2541 | else => unreachable, | 2535 | else => unreachable, |
| 2542 | }; | 2536 | }; |
| ... | @@ -5134,7 +5128,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -5134,7 +5128,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 5134 | return .{ .imm32 = 0xaaaaaaaa }; | 5128 | return .{ .imm32 = 0xaaaaaaaa }; |
| 5135 | }, | 5129 | }, |
| 5136 | .@"struct", .@"union" => { | 5130 | .@"struct", .@"union" => { |
| 5137 | const backing_int_ty = ty.bitpackBackingInt(zcu); | 5131 | const backing_int_ty = ty.backingIntType(zcu); |
| 5138 | return cg.emitUndefined(backing_int_ty); | 5132 | return cg.emitUndefined(backing_int_ty); |
| 5139 | }, | 5133 | }, |
| 5140 | else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}), | 5134 | else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}), |
src/codegen/x86_64/CodeGen.zig+2| ... | @@ -63,6 +63,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -63,6 +63,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 63 | .reduce_one_elem_to_bit_cast, | 63 | .reduce_one_elem_to_bit_cast, |
| 64 | .splat_one_elem_to_bit_cast, | 64 | .splat_one_elem_to_bit_cast, |
| 65 | 65 | ||
| 66 | .expand_bit_cast_safe, | ||
| 66 | .expand_int_cast_safe, | 67 | .expand_int_cast_safe, |
| 67 | .expand_int_from_float_safe, | 68 | .expand_int_from_float_safe, |
| 68 | .expand_int_from_float_optimized_safe, | 69 | .expand_int_from_float_optimized_safe, |
| ... | @@ -67446,6 +67447,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -67446,6 +67447,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 67446 | .int_from_error, | 67447 | .int_from_error, |
| 67447 | .union_from_enum, | 67448 | .union_from_enum, |
| 67448 | => try cg.airBitCast(inst), | 67449 | => try cg.airBitCast(inst), |
| 67450 | .bit_cast_safe => unreachable, | ||
| 67449 | .block => { | 67451 | .block => { |
| 67450 | const block = cg.air.unwrapBlock(inst); | 67452 | const block = cg.air.unwrapBlock(inst); |
| 67451 | if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none); | 67453 | if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none); |
src/link/Dwarf.zig+1| ... | @@ -5098,6 +5098,7 @@ fn DeclValEnum(comptime T: type) type { | ... | @@ -5098,6 +5098,7 @@ fn DeclValEnum(comptime T: type) type { |
| 5098 | if (min_value == null or min_value.? > value) min_value = value; | 5098 | if (min_value == null or min_value.? > value) min_value = value; |
| 5099 | if (max_value == null or max_value.? < value) max_value = value; | 5099 | if (max_value == null or max_value.? < value) max_value = value; |
| 5100 | } | 5100 | } |
| 5101 | if (fields_len == 0) return enum {}; | ||
| 5101 | const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0); | 5102 | const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0); |
| 5102 | var field_vals: [fields_len]TagInt = undefined; | 5103 | var field_vals: [fields_len]TagInt = undefined; |
| 5103 | for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name); | 5104 | for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name); |
src/link/Wasm/Flush.zig+1-1| ... | @@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 151 | assert(ip.indexToKey(data.ip_index) == .enum_type); | 151 | assert(ip.indexToKey(data.ip_index) == .enum_type); |
| 152 | const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index); | 152 | const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index); |
| 153 | if (!gop.found_existing) { | 153 | if (!gop.found_existing) { |
| 154 | const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu); | 154 | const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).backingIntType(zcu); |
| 155 | gop.value_ptr.* = .{ .tag_name = .{ | 155 | gop.value_ptr.* = .{ .tag_name = .{ |
| 156 | .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}), | 156 | .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}), |
| 157 | .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target), | 157 | .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target), |
src/print_value.zig+18-12| ... | @@ -94,15 +94,14 @@ pub fn print( | ... | @@ -94,15 +94,14 @@ pub fn print( |
| 94 | enum_literal.fmt(ip), | 94 | enum_literal.fmt(ip), |
| 95 | }), | 95 | }), |
| 96 | .enum_tag => |enum_tag| { | 96 | .enum_tag => |enum_tag| { |
| 97 | const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern()); | 97 | const ty: Type = .fromInterned(enum_tag.ty); |
| 98 | if (enum_type.tagValueIndex(ip, enum_tag.int)) |tag_index| { | 98 | const enum_obj = ip.loadEnumType(ty.toIntern()); |
| 99 | return writer.print(".{f}", .{enum_type.field_names.get(ip)[tag_index].fmt(ip)}); | 99 | if (enum_obj.tagValueIndex(ip, enum_tag.int)) |tag_index| { |
| 100 | return writer.print(".{f}", .{enum_obj.field_names.get(ip)[tag_index].fmt(ip)}); | ||
| 100 | } | 101 | } |
| 101 | if (level == 0) { | 102 | try writer.writeAll("@fromBackingInt("); |
| 102 | return writer.writeAll("@enumFromInt(...)"); | 103 | if (level == 0) return writer.writeAll("...)"); |
| 103 | } | 104 | try print(.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema); |
| 104 | try writer.writeAll("@enumFromInt("); | ||
| 105 | try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema); | ||
| 106 | try writer.writeAll(")"); | 105 | try writer.writeAll(")"); |
| 107 | }, | 106 | }, |
| 108 | .float => |float| switch (float.storage) { | 107 | .float => |float| switch (float.storage) { |
| ... | @@ -190,10 +189,17 @@ pub fn print( | ... | @@ -190,10 +189,17 @@ pub fn print( |
| 190 | try writer.writeAll(" }"); | 189 | try writer.writeAll(" }"); |
| 191 | return; | 190 | return; |
| 192 | }, | 191 | }, |
| 193 | .@"union" => { | 192 | .@"union" => switch (ty.backingIntMode(zcu)) { |
| 194 | try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)}); | 193 | .auto => { |
| 195 | try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema); | 194 | try writer.print("@bitCast(@as({f}, ", .{ty.backingIntType(zcu).fmt(pt)}); |
| 196 | try writer.writeAll("))"); | 195 | try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema); |
| 196 | try writer.writeAll("))"); | ||
| 197 | }, | ||
| 198 | .explicit => { | ||
| 199 | try writer.writeAll("@fromBackingInt("); | ||
| 200 | try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema); | ||
| 201 | try writer.writeAll(")"); | ||
| 202 | }, | ||
| 197 | }, | 203 | }, |
| 198 | else => unreachable, | 204 | else => unreachable, |
| 199 | } | 205 | } |
src/print_zir.zig+14| ... | @@ -193,6 +193,7 @@ const Writer = struct { | ... | @@ -193,6 +193,7 @@ const Writer = struct { |
| 193 | .elem_type, | 193 | .elem_type, |
| 194 | .indexable_ptr_elem_type, | 194 | .indexable_ptr_elem_type, |
| 195 | .splat_op_result_ty, | 195 | .splat_op_result_ty, |
| 196 | .from_backing_int_arg_ty, | ||
| 196 | .indexable_ptr_len, | 197 | .indexable_ptr_len, |
| 197 | .anyframe_type, | 198 | .anyframe_type, |
| 198 | .bit_not, | 199 | .bit_not, |
| ... | @@ -232,6 +233,7 @@ const Writer = struct { | ... | @@ -232,6 +233,7 @@ const Writer = struct { |
| 232 | .compile_error, | 233 | .compile_error, |
| 233 | .set_eval_branch_quota, | 234 | .set_eval_branch_quota, |
| 234 | .int_from_enum, | 235 | .int_from_enum, |
| 236 | .backing_int, | ||
| 235 | .align_of, | 237 | .align_of, |
| 236 | .int_from_bool, | 238 | .int_from_bool, |
| 237 | .embed_file, | 239 | .embed_file, |
| ... | @@ -418,6 +420,8 @@ const Writer = struct { | ... | @@ -418,6 +420,8 @@ const Writer = struct { |
| 418 | 420 | ||
| 419 | .for_len => try self.writePlNodeMultiOp(stream, inst), | 421 | .for_len => try self.writePlNodeMultiOp(stream, inst), |
| 420 | 422 | ||
| 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), |
| 422 | 426 | ||
| 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 | } |
| 987 | 991 | ||
| 992 | fn writeFromBackingInt(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { | ||
| 993 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | ||
| 994 | const extra = self.code.extraData(Zir.Inst.FromBackingInt, inst_data.payload_index); | ||
| 995 | try self.writeInstRef(stream, extra.data.result_type); | ||
| 996 | try stream.writeAll(", "); | ||
| 997 | try self.writeBracedBody(stream, self.code.bodySlice(extra.end, extra.data.body_len)); | ||
| 998 | try stream.writeAll(") "); | ||
| 999 | try self.writeSrcNode(stream, inst_data.src_node); | ||
| 1000 | } | ||
| 1001 | |||
| 988 | fn writeBuiltinCall(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { | 1002 | fn writeBuiltinCall(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { |
| 989 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | 1003 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 990 | const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; | 1004 | const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ | |||
test/behavior.zig+1| ... | @@ -6,6 +6,7 @@ test { | ... | @@ -6,6 +6,7 @@ test { |
| 6 | _ = @import("behavior/alignof.zig"); | 6 | _ = @import("behavior/alignof.zig"); |
| 7 | _ = @import("behavior/array.zig"); | 7 | _ = @import("behavior/array.zig"); |
| 8 | _ = @import("behavior/atomics.zig"); | 8 | _ = @import("behavior/atomics.zig"); |
| 9 | _ = @import("behavior/backing_int.zig"); | ||
| 9 | _ = @import("behavior/basic.zig"); | 10 | _ = @import("behavior/basic.zig"); |
| 10 | _ = @import("behavior/bit_shifting.zig"); | 11 | _ = @import("behavior/bit_shifting.zig"); |
| 11 | _ = @import("behavior/bitcast.zig"); | 12 | _ = @import("behavior/bitcast.zig"); |
test/behavior/backing_int.zig created+384| ... | @@ -0,0 +1,384 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | |||
| 6 | const 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 | }; | ||
| 16 | const 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 | }; | ||
| 25 | const 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 | }; | ||
| 33 | const 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 | }; | ||
| 40 | const 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 | |||
| 48 | test "@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 | |||
| 76 | test "@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 | |||
| 105 | const 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 | }; | ||
| 115 | const 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 | }; | ||
| 124 | const 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 | }; | ||
| 131 | const 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 | |||
| 139 | test "@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 | |||
| 163 | const 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 | }; | ||
| 171 | const 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 | }; | ||
| 179 | const 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 | }; | ||
| 193 | const 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 | }; | ||
| 200 | const 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 | |||
| 208 | test "@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 | |||
| 236 | test "@fromBackingInt with packed structs" { | ||
| 237 | const static = struct { | ||
| 238 | fn doTheTest( | ||
| 239 | b1: @typeInfo(S1).@"struct".backing_integer.?, | ||
| 240 | b2: @typeInfo(S2).@"struct".backing_integer.?, | ||
| 241 | b3: @typeInfo(S3).@"struct".backing_integer.?, | ||
| 242 | b4: @typeInfo(S4).@"struct".backing_integer.?, | ||
| 243 | b5: @typeInfo(S5).@"struct".backing_integer.?, | ||
| 244 | ) !void { | ||
| 245 | const v1: S1 = @fromBackingInt(b1); | ||
| 246 | try expect(v1 == S1.expected.val); | ||
| 247 | |||
| 248 | const v2: S2 = @fromBackingInt(b2); | ||
| 249 | try expect(v2 == S2.expected.val); | ||
| 250 | |||
| 251 | const v3: S3 = @fromBackingInt(b3); | ||
| 252 | try expect(v3 == S3.expected.val); | ||
| 253 | |||
| 254 | // https://codeberg.org/ziglang/zig/issues/35982 | ||
| 255 | if (builtin.zig_backend != .stage2_x86_64) { | ||
| 256 | const v4: S4 = @fromBackingInt(b4); | ||
| 257 | try expect(v4 == S4.expected.val); | ||
| 258 | } | ||
| 259 | |||
| 260 | const v5: S5 = @fromBackingInt(b5); | ||
| 261 | try expect(v5 == S5.expected.val); | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | try static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int); | ||
| 265 | try comptime static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int); | ||
| 266 | } | ||
| 267 | |||
| 268 | const U1 = packed union(u8) { | ||
| 269 | a: u8, | ||
| 270 | b: i8, | ||
| 271 | const expected = .{ | ||
| 272 | .val = @as(U1, .{ .b = -123 }), | ||
| 273 | .int = @as(@typeInfo(U1).@"union".backing_integer.?, @bitCast(@as(i8, -123))), | ||
| 274 | }; | ||
| 275 | }; | ||
| 276 | const U2 = packed union(i20) { | ||
| 277 | a: u20, | ||
| 278 | b: enum(i20) { x, y, z }, | ||
| 279 | const expected = .{ | ||
| 280 | .val = @as(U2, .{ .b = .z }), | ||
| 281 | .int = @as(@typeInfo(U2).@"union".backing_integer.?, 0b00000000000000000000000000000010), | ||
| 282 | }; | ||
| 283 | }; | ||
| 284 | const U3 = packed union(i32) { | ||
| 285 | a: packed struct(u32) { | ||
| 286 | x: u18, | ||
| 287 | y: i14, | ||
| 288 | }, | ||
| 289 | b: packed union(u32) { | ||
| 290 | x: i32, | ||
| 291 | y: enum(u32) { u, v }, | ||
| 292 | }, | ||
| 293 | const expected = .{ | ||
| 294 | .val = @as(U3, .{ .b = .{ .y = .v } }), | ||
| 295 | .int = @as(@typeInfo(U3).@"union".backing_integer.?, 0b00000000000000000000000000000001), | ||
| 296 | }; | ||
| 297 | }; | ||
| 298 | const U4 = packed union(i200) { | ||
| 299 | a: u200, | ||
| 300 | const expected = .{ | ||
| 301 | .val = @as(U4, .{ .a = (1 << 199) + 10 }), | ||
| 302 | .int = @as(@typeInfo(U4).@"union".backing_integer.?, @bitCast(@as(u200, (1 << 199) + 10))), | ||
| 303 | }; | ||
| 304 | }; | ||
| 305 | const U5 = packed union(u0) { | ||
| 306 | a: u0, | ||
| 307 | const expected = .{ | ||
| 308 | .val = @as(U5, .{ .a = 0 }), | ||
| 309 | .int = @as(@typeInfo(U5).@"union".backing_integer.?, 0), | ||
| 310 | }; | ||
| 311 | }; | ||
| 312 | |||
| 313 | test "@backingInt with packed unions" { | ||
| 314 | const static = struct { | ||
| 315 | fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void { | ||
| 316 | const b1 = @backingInt(v1); | ||
| 317 | comptime assert(@TypeOf(b1) == @typeInfo(U1).@"union".backing_integer.?); | ||
| 318 | try expect(b1 == U1.expected.int); | ||
| 319 | |||
| 320 | const b2 = @backingInt(v2); | ||
| 321 | comptime assert(@TypeOf(b2) == @typeInfo(U2).@"union".backing_integer.?); | ||
| 322 | try expect(b2 == U2.expected.int); | ||
| 323 | |||
| 324 | const b3 = @backingInt(v3); | ||
| 325 | comptime assert(@TypeOf(b3) == @typeInfo(U3).@"union".backing_integer.?); | ||
| 326 | try expect(b3 == U3.expected.int); | ||
| 327 | |||
| 328 | const b4 = @backingInt(v4); | ||
| 329 | comptime assert(@TypeOf(b4) == @typeInfo(U4).@"union".backing_integer.?); | ||
| 330 | try expect(b4 == U4.expected.int); | ||
| 331 | |||
| 332 | const b5 = @backingInt(v5); | ||
| 333 | comptime assert(@TypeOf(b5) == @typeInfo(U5).@"union".backing_integer.?); | ||
| 334 | try expect(b5 == U5.expected.int); | ||
| 335 | } | ||
| 336 | }; | ||
| 337 | try static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val); | ||
| 338 | try comptime static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val); | ||
| 339 | } | ||
| 340 | |||
| 341 | test "@fromBackingInt with packed unions" { | ||
| 342 | const static = struct { | ||
| 343 | fn doTheTest( | ||
| 344 | b1: @typeInfo(U1).@"union".backing_integer.?, | ||
| 345 | b2: @typeInfo(U2).@"union".backing_integer.?, | ||
| 346 | b3: @typeInfo(U3).@"union".backing_integer.?, | ||
| 347 | b4: @typeInfo(U4).@"union".backing_integer.?, | ||
| 348 | b5: @typeInfo(U5).@"union".backing_integer.?, | ||
| 349 | ) !void { | ||
| 350 | const v1: U1 = @fromBackingInt(b1); | ||
| 351 | try expect(v1 == U1.expected.val); | ||
| 352 | |||
| 353 | const v2: U2 = @fromBackingInt(b2); | ||
| 354 | try expect(v2 == U2.expected.val); | ||
| 355 | |||
| 356 | const v3: U3 = @fromBackingInt(b3); | ||
| 357 | try expect(v3 == U3.expected.val); | ||
| 358 | |||
| 359 | // https://codeberg.org/ziglang/zig/issues/35982 | ||
| 360 | if (builtin.zig_backend != .stage2_x86_64) { | ||
| 361 | const v4: U4 = @fromBackingInt(b4); | ||
| 362 | try expect(v4 == U4.expected.val); | ||
| 363 | } | ||
| 364 | |||
| 365 | const v5: U5 = @fromBackingInt(b5); | ||
| 366 | try expect(v5 == U5.expected.val); | ||
| 367 | } | ||
| 368 | }; | ||
| 369 | try static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int); | ||
| 370 | try comptime static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int); | ||
| 371 | } | ||
| 372 | |||
| 373 | test "@fromBackingInt provides result type to its argument" { | ||
| 374 | const E = enum(u32) { a, b, c }; | ||
| 375 | const static = struct { | ||
| 376 | fn doTheTest(x: u8) !void { | ||
| 377 | const e: E = @fromBackingInt(x); | ||
| 378 | try expect(e == .b); | ||
| 379 | try expect(@backingInt(e) == x); | ||
| 380 | } | ||
| 381 | }; | ||
| 382 | try static.doTheTest(1); | ||
| 383 | try comptime static.doTheTest(1); | ||
| 384 | } | ||
test/behavior/enum.zig+15-23| ... | @@ -1451,29 +1451,6 @@ test "comptime @enumFromInt with signed arithmetic" { | ... | @@ -1451,29 +1451,6 @@ test "comptime @enumFromInt with signed arithmetic" { |
| 1451 | comptime assert(@intFromEnum(x) == 0); | 1451 | comptime assert(@intFromEnum(x) == 0); |
| 1452 | } | 1452 | } |
| 1453 | 1453 | ||
| 1454 | test "switch on empty enum" { | ||
| 1455 | const E = enum {}; | ||
| 1456 | var e: E = undefined; | ||
| 1457 | _ = &e; | ||
| 1458 | switch (e) {} | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | test "switch on empty enum with a specified tag type" { | ||
| 1462 | const E = enum(u8) {}; | ||
| 1463 | var e: E = undefined; | ||
| 1464 | _ = &e; | ||
| 1465 | switch (e) {} | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | test "empty enum passed as argument" { | ||
| 1469 | const E = enum { | ||
| 1470 | fn f(e: @This()) void { | ||
| 1471 | switch (e) {} | ||
| 1472 | } | ||
| 1473 | }; | ||
| 1474 | E.f(@as(E, undefined)); | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | test "enum int tag type uses declaration inside the enum" { | 1454 | test "enum int tag type uses declaration inside the enum" { |
| 1478 | const static = struct { | 1455 | const static = struct { |
| 1479 | const E = enum(E.IntTag) { | 1456 | const E = enum(E.IntTag) { |
| ... | @@ -1488,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" { | ... | @@ -1488,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" { |
| 1488 | try expect(val == .b); | 1465 | try expect(val == .b); |
| 1489 | try expect(@intFromEnum(val) == 1); | 1466 | try expect(@intFromEnum(val) == 1); |
| 1490 | } | 1467 | } |
| 1468 | |||
| 1469 | test "convert from/to backing int" { | ||
| 1470 | const E = enum(u33) { | ||
| 1471 | a, | ||
| 1472 | b, | ||
| 1473 | c, | ||
| 1474 | fn doTheTest(s: @This()) !void { | ||
| 1475 | const backing_int = @backingInt(s); | ||
| 1476 | const reconstructed: @This() = @fromBackingInt(backing_int); | ||
| 1477 | try expect(reconstructed == s); | ||
| 1478 | } | ||
| 1479 | }; | ||
| 1480 | try E.doTheTest(.b); | ||
| 1481 | try comptime E.doTheTest(.b); | ||
| 1482 | } |
test/behavior/packed-struct.zig+36-8| ... | @@ -122,7 +122,7 @@ test "correct sizeOf and offsets in packed structs" { | ... | @@ -122,7 +122,7 @@ test "correct sizeOf and offsets in packed structs" { |
| 122 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 122 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 123 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 123 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 124 | 124 | ||
| 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)); |
| 164 | 164 | ||
| 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); |
| 178 | 178 | ||
| 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 }; |
| 192 | 192 | ||
| 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 }; |
| 195 | 195 | ||
| 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")); |
| 201 | 201 | ||
| 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/16615 | 558 | // 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; |
| 560 | 560 | ||
| 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); |
| 574 | 574 | ||
| 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 | } |
| 580 | 580 | ||
| 581 | test "nested packed struct field access test" { | 581 | test "nested packed struct field access test" { |
| ... | @@ -1246,3 +1246,31 @@ test "initialize packed struct field to undefined at comptime" { | ... | @@ -1246,3 +1246,31 @@ test "initialize packed struct field to undefined at comptime" { |
| 1246 | const val: S = .{ .x = undefined }; | 1246 | const val: S = .{ .x = undefined }; |
| 1247 | _ = val; | 1247 | _ = val; |
| 1248 | } | 1248 | } |
| 1249 | |||
| 1250 | test "convert from/to backing int" { | ||
| 1251 | const S = packed struct(u33) { | ||
| 1252 | a: u7, | ||
| 1253 | b: enum(u10) { x, y, z }, | ||
| 1254 | c: f16, | ||
| 1255 | fn doTheTest(s: @This()) !void { | ||
| 1256 | const backing_int = @backingInt(s); | ||
| 1257 | const reconstructed: @This() = @fromBackingInt(backing_int); | ||
| 1258 | try expect(reconstructed == s); | ||
| 1259 | } | ||
| 1260 | }; | ||
| 1261 | try S.doTheTest(.{ .a = 123, .b = .y, .c = 0.23 }); | ||
| 1262 | try comptime S.doTheTest(.{ .a = 123, .b = .y, .c = 0.23 }); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | test "equality with wide backing integer" { | ||
| 1266 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982 | ||
| 1267 | |||
| 1268 | const S = packed struct(i200) { | ||
| 1269 | x: u200, | ||
| 1270 | fn doTheTest(s: @This(), int: i200) !void { | ||
| 1271 | try expect(s == @as(@This(), @bitCast(int))); | ||
| 1272 | } | ||
| 1273 | }; | ||
| 1274 | try S.doTheTest(.{ .x = (1 << 200) - 1 }, -1); | ||
| 1275 | try comptime S.doTheTest(.{ .x = (1 << 200) - 1 }, -1); | ||
| 1276 | } |
test/behavior/packed-union.zig+27| ... | @@ -227,3 +227,30 @@ test "initialize packed union field to undefined at comptime" { | ... | @@ -227,3 +227,30 @@ test "initialize packed union field to undefined at comptime" { |
| 227 | const val: U = .{ .x = undefined }; | 227 | const val: U = .{ .x = undefined }; |
| 228 | _ = val; | 228 | _ = val; |
| 229 | } | 229 | } |
| 230 | |||
| 231 | test "convert from/to backing int" { | ||
| 232 | const U = packed union(u10) { | ||
| 233 | a: i10, | ||
| 234 | b: enum(u10) { x, y, z }, | ||
| 235 | fn doTheTest(u: @This()) !void { | ||
| 236 | const backing_int = @backingInt(u); | ||
| 237 | const reconstructed: @This() = @fromBackingInt(backing_int); | ||
| 238 | try expect(reconstructed == u); | ||
| 239 | } | ||
| 240 | }; | ||
| 241 | try U.doTheTest(.{ .a = 123 }); | ||
| 242 | try comptime U.doTheTest(.{ .a = 123 }); | ||
| 243 | } | ||
| 244 | |||
| 245 | test "equality with wide backing integer" { | ||
| 246 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982 | ||
| 247 | |||
| 248 | const U = packed union(i200) { | ||
| 249 | x: u200, | ||
| 250 | fn doTheTest(s: @This(), int: i200) !void { | ||
| 251 | try expect(s == @as(@This(), @bitCast(int))); | ||
| 252 | } | ||
| 253 | }; | ||
| 254 | try U.doTheTest(.{ .x = (1 << 200) - 1 }, -1); | ||
| 255 | try comptime U.doTheTest(.{ .x = (1 << 200) - 1 }, -1); | ||
| 256 | } |
test/behavior/switch.zig+2-2| ... | @@ -1132,13 +1132,13 @@ test "decl literals as switch cases" { | ... | @@ -1132,13 +1132,13 @@ test "decl literals as switch cases" { |
| 1132 | try comptime E.doTheTest(.foo); | 1132 | try comptime E.doTheTest(.foo); |
| 1133 | } | 1133 | } |
| 1134 | 1134 | ||
| 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. |
| 1137 | test "switch with uninstantiable union fields" { | 1137 | test "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 {}, |
| 1142 | 1142 | ||
| 1143 | fn doTheTest(u: @This()) void { | 1143 | fn doTheTest(u: @This()) void { |
| 1144 | switch (u) { | 1144 | switch (u) { |
test/behavior/type.zig+11-3| ... | @@ -266,13 +266,21 @@ test "Type.Union from regular enum" { | ... | @@ -266,13 +266,21 @@ test "Type.Union from regular enum" { |
| 266 | test "Type.Union from empty regular enum" { | 266 | test "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 | } |
| 271 | 275 | ||
| 272 | test "Type.Union from empty Type.Enum" { | 276 | test "Type.Union from empty Type.Enum" { |
| 273 | const E = @Enum(u0, .exhaustive, &.{}, &.{}); | 277 | const E = @Enum(noreturn, .exhaustive, &.{}, &.{}); |
| 274 | const U = @Union(.auto, E, &.{}, &.{}, &.{}); | 278 | const U = @Union(.auto, E, &.{}, &.{}, &.{}); |
| 275 | try testing.expectEqual(@typeInfo(U).@"union".field_names.len, 0); | 279 | |
| 280 | const info = @typeInfo(U).@"union"; | ||
| 281 | try testing.expect(info.field_names.len == 0); | ||
| 282 | try testing.expect(info.tag_type != null); | ||
| 283 | try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn); | ||
| 276 | } | 284 | } |
| 277 | 285 | ||
| 278 | test "Type.Fn" { | 286 | test "Type.Fn" { |
test/cases/compile_errors/backing_int_invalid_arg_type.zig created+38| ... | @@ -0,0 +1,38 @@ | ||
| 1 | const S1 = extern struct { x: u32 }; | ||
| 2 | export fn entry1(x: S1) u32 { | ||
| 3 | return @backingInt(x); | ||
| 4 | } | ||
| 5 | |||
| 6 | const U1 = extern union { x: u32 }; | ||
| 7 | export fn entry2(x: U1) u32 { | ||
| 8 | return @backingInt(x); | ||
| 9 | } | ||
| 10 | |||
| 11 | export fn entry3(x: u32) u32 { | ||
| 12 | return @backingInt(x); | ||
| 13 | } | ||
| 14 | |||
| 15 | const S2 = packed struct { x: u32 }; | ||
| 16 | export fn entry4(x: u32) u32 { | ||
| 17 | return @backingInt(@as(S2, .{ .x = x })); | ||
| 18 | } | ||
| 19 | |||
| 20 | const U2 = packed union { x: u32 }; | ||
| 21 | export 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 @@ | ||
| 1 | export fn foo() void { | ||
| 2 | const S = struct { | ||
| 3 | f: u8, | ||
| 4 | }; | ||
| 5 | _ = @as([@sizeOf(S)]u8, @bitCast([1]S{undefined})); | ||
| 6 | } | ||
| 7 | |||
| 8 | export fn bar() void { | ||
| 9 | const S = struct { | ||
| 10 | f: u8, | ||
| 11 | }; | ||
| 12 | _ = @as([1]S, @bitCast(@as([@sizeOf(S)]u8, undefined))); | ||
| 13 | } | ||
| 14 | |||
| 15 | export 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 @@ | ||
| 1 | const y: u32 = 0; | ||
| 2 | |||
| 3 | export fn entry1() void { | ||
| 4 | _ = @as(comptime_float, @bitCast(y)); | ||
| 5 | } | ||
| 6 | export fn entry2() void { | ||
| 7 | const x: comptime_float = undefined; | ||
| 8 | _ = @as(u32, @bitCast(x)); | ||
| 9 | } | ||
| 10 | |||
| 11 | export fn entry3() void { | ||
| 12 | _ = @as(comptime_int, @bitCast(y)); | ||
| 13 | } | ||
| 14 | export fn entry4() void { | ||
| 15 | const x: comptime_int = undefined; | ||
| 16 | _ = @as(u32, @bitCast(x)); | ||
| 17 | } | ||
| 18 | |||
| 19 | export fn entry5() void { | ||
| 20 | _ = @as(@EnumLiteral(), @bitCast(y)); | ||
| 21 | } | ||
| 22 | export fn entry6() void { | ||
| 23 | const x: @EnumLiteral() = undefined; | ||
| 24 | _ = @as(u32, @bitCast(x)); | ||
| 25 | } | ||
| 26 | |||
| 27 | export fn entry7() void { | ||
| 28 | _ = @as(error{}, @bitCast(y)); | ||
| 29 | } | ||
| 30 | export fn entry8() void { | ||
| 31 | const x: error{} = undefined; | ||
| 32 | _ = @as(u32, @bitCast(x)); | ||
| 33 | } | ||
| 34 | |||
| 35 | export fn entry9() void { | ||
| 36 | _ = @as(?(anyerror!u32), @bitCast(y)); | ||
| 37 | } | ||
| 38 | export fn entry10() void { | ||
| 39 | const x: anyerror!u32 = undefined; | ||
| 40 | _ = @as(u32, @bitCast(x)); | ||
| 41 | } | ||
| 42 | |||
| 43 | export fn entry11() void { | ||
| 44 | _ = @as(fn () void, @bitCast(y)); | ||
| 45 | } | ||
| 46 | export fn entry12() void { | ||
| 47 | const x: fn () void = undefined; | ||
| 48 | _ = @as(u32, @bitCast(x)); | ||
| 49 | } | ||
| 50 | |||
| 51 | export fn entry13() void { | ||
| 52 | _ = @as(noreturn, @bitCast(y)); | ||
| 53 | } | ||
| 54 | |||
| 55 | export fn entry14() void { | ||
| 56 | _ = @as(@TypeOf(null), @bitCast(y)); | ||
| 57 | } | ||
| 58 | export fn entry15() void { | ||
| 59 | const x: @TypeOf(null) = undefined; | ||
| 60 | _ = @as(u32, @bitCast(x)); | ||
| 61 | } | ||
| 62 | |||
| 63 | export fn entry16() void { | ||
| 64 | const O = opaque {}; | ||
| 65 | _ = @as(O, @bitCast(y)); | ||
| 66 | } | ||
| 67 | |||
| 68 | export fn entry17() void { | ||
| 69 | _ = @as(??u32, @bitCast(y)); | ||
| 70 | } | ||
| 71 | export fn entry18() void { | ||
| 72 | const x: ?u32 = undefined; | ||
| 73 | _ = @as(u32, @bitCast(x)); | ||
| 74 | } | ||
| 75 | |||
| 76 | export fn entry19() void { | ||
| 77 | _ = @as(type, @bitCast(y)); | ||
| 78 | } | ||
| 79 | export fn entry20() void { | ||
| 80 | const x: type = undefined; | ||
| 81 | _ = @as(u32, @bitCast(x)); | ||
| 82 | } | ||
| 83 | |||
| 84 | export fn entry21() void { | ||
| 85 | _ = @as(@TypeOf(undefined), @bitCast(y)); | ||
| 86 | } | ||
| 87 | export fn entry22() void { | ||
| 88 | const x: @TypeOf(undefined) = undefined; | ||
| 89 | _ = @as(u32, @bitCast(x)); | ||
| 90 | } | ||
| 91 | |||
| 92 | export fn entry23() void { | ||
| 93 | _ = @as(void, @bitCast(y)); | ||
| 94 | } | ||
| 95 | export fn entry24() void { | ||
| 96 | const x: void = undefined; | ||
| 97 | _ = @as(u32, @bitCast(x)); | ||
| 98 | } | ||
| 99 | |||
| 100 | export fn entry25() void { | ||
| 101 | _ = @as(*u8, @bitCast(y)); | ||
| 102 | } | ||
| 103 | export fn entry26() void { | ||
| 104 | const x: *u8 = undefined; | ||
| 105 | _ = @as(u32, @bitCast(x)); | ||
| 106 | } | ||
| 107 | |||
| 108 | export fn entry27() void { | ||
| 109 | _ = @as(*u8, @bitCast(@as(*u32, @ptrFromInt(y)))); | ||
| 110 | } | ||
| 111 | export fn entry28() void { | ||
| 112 | const x: *u8 = undefined; | ||
| 113 | _ = @as(*u32, @bitCast(x)); | ||
| 114 | } | ||
| 115 | |||
| 116 | export fn entry29() void { | ||
| 117 | const S = struct { x: u32 }; | ||
| 118 | _ = @as(S, @bitCast(y)); | ||
| 119 | } | ||
| 120 | export fn entry30() void { | ||
| 121 | const S = struct { x: u32 }; | ||
| 122 | const x: S = undefined; | ||
| 123 | _ = @as(u32, @bitCast(x)); | ||
| 124 | } | ||
| 125 | |||
| 126 | export fn entry31() void { | ||
| 127 | const U = union { x: u32 }; | ||
| 128 | _ = @as(U, @bitCast(y)); | ||
| 129 | } | ||
| 130 | export fn entry32() void { | ||
| 131 | const U = union { x: u32 }; | ||
| 132 | const x: U = undefined; | ||
| 133 | _ = @as(u32, @bitCast(x)); | ||
| 134 | } | ||
| 135 | |||
| 136 | export fn entry33() void { | ||
| 137 | const S = struct { x: u32 }; | ||
| 138 | _ = @as([10]S, @bitCast(y)); | ||
| 139 | } | ||
| 140 | export fn entry34() void { | ||
| 141 | const S = struct { x: u32 }; | ||
| 142 | const x: [10]S = undefined; | ||
| 143 | _ = @as(u32, @bitCast(x)); | ||
| 144 | } | ||
| 145 | |||
| 146 | export fn entry35() void { | ||
| 147 | const E = enum {}; | ||
| 148 | _ = @as(E, @bitCast(y)); | ||
| 149 | } | ||
| 150 | |||
| 151 | export 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 @@ | ||
| 1 | const E = enum(u8) { a, b, c }; | ||
| 2 | export fn entry1() void { | ||
| 3 | const x: E = @bitCast(@as(u8, 3)); | ||
| 4 | _ = x; | ||
| 5 | } | ||
| 6 | |||
| 7 | export 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 @@ | ||
| 1 | const E = enum(noreturn) {}; | ||
| 2 | |||
| 3 | export fn entry1() void { | ||
| 4 | const e: E = @fromBackingInt(undefined); | ||
| 5 | _ = e; | ||
| 6 | } | ||
| 7 | |||
| 8 | export fn entry2() void { | ||
| 9 | const e: E = @fromBackingInt(0); | ||
| 10 | _ = e; | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // | ||
| 15 | // :4:34: error: expected type 'noreturn', found '@TypeOf(undefined)' | ||
| 16 | // :4:34: note: cannot coerce to uninstantiable type 'noreturn' | ||
| 17 | // :9:34: error: expected type 'noreturn', found 'comptime_int' | ||
| 18 | // :9:34: note: cannot coerce to uninstantiable type 'noreturn' | ||
test/cases/compile_errors/enum_noreturn_backing_type.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const E1 = enum(u32) {}; | ||
| 2 | export fn entry1() void { | ||
| 3 | const e: E1 = undefined; | ||
| 4 | _ = e; | ||
| 5 | } | ||
| 6 | |||
| 7 | const E2 = enum(noreturn) { a, b, c }; | ||
| 8 | export fn entry2() void { | ||
| 9 | const e: E2 = undefined; | ||
| 10 | _ = e; | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // | ||
| 15 | // :1:17: error: empty exhaustive enums must be backed by 'noreturn' | ||
| 16 | // :7:17: error: non-empty enums cannot be backed by 'noreturn' | ||
test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig+19-11| ... | @@ -1,15 +1,23 @@ | ... | @@ -1,15 +1,23 @@ |
| 1 | pub const E = enum(u31) { A, B, C }; | 1 | export fn entry1() void { |
| 2 | pub const S = extern struct { | 2 | const E = enum(u31) { A, B, C }; |
| 3 | e: E, | 3 | _ = @sizeOf(extern struct { |
| 4 | }; | 4 | x: E, |
| 5 | export fn entry() void { | 5 | }); |
| 6 | const s: S = undefined; | 6 | } |
| 7 | _ = s; | 7 | export fn entry2() void { |
| 8 | const E = enum(noreturn) {}; | ||
| 9 | _ = @sizeOf(extern struct { | ||
| 10 | x: E, | ||
| 11 | }); | ||
| 8 | } | 12 | } |
| 9 | 13 | ||
| 10 | // error | 14 | // error |
| 11 | // | 15 | // |
| 12 | // :3:8: error: extern structs cannot contain fields of type 'tmp.E' | 16 | // :4:12: error: extern structs cannot contain fields of type 'tmp.entry1.E' |
| 13 | // :1:15: note: enum tag type 'u31' is not extern compatible | 17 | // :2:15: note: enum tag type 'u31' is not extern compatible |
| 14 | // :1:15: note: only integers with 0 or power of two bits are extern compatible | 18 | // :2:15: note: only integers with 0 or power of two bits are extern compatible |
| 15 | // :1:15: note: enum declared here | 19 | // :2:15: note: enum declared here |
| 20 | // :10:12: error: extern structs cannot contain fields of type 'tmp.entry2.E' | ||
| 21 | // :8:15: note: enum tag type 'noreturn' is not extern compatible | ||
| 22 | // :8:15: note: 'noreturn' is only allowed as a return type | ||
| 23 | // :8:15: note: enum declared here |
test/cases/compile_errors/from_backing_int_invalid_dest_type.zig created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | const S1 = extern struct { x: u32 }; | ||
| 2 | export fn entry1(x: u32) void { | ||
| 3 | _ = @as(S1, @fromBackingInt(x)); | ||
| 4 | } | ||
| 5 | |||
| 6 | const U1 = extern union { x: u32 }; | ||
| 7 | export fn entry2(x: u32) void { | ||
| 8 | _ = @as(U1, @fromBackingInt(x)); | ||
| 9 | } | ||
| 10 | |||
| 11 | export fn entry3(x: u32) void { | ||
| 12 | _ = @as(u32, @fromBackingInt(x)); | ||
| 13 | } | ||
| 14 | |||
| 15 | const S2 = packed struct { x: u32 }; | ||
| 16 | export fn entry4(x: u32) void { | ||
| 17 | _ = @as(S2, @fromBackingInt(x)); | ||
| 18 | } | ||
| 19 | |||
| 20 | const U2 = packed union { x: u32 }; | ||
| 21 | export 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 @@ | ||
| 1 | const E = enum(u32) { a, b, c }; | ||
| 2 | export fn entry1(x: u64) E { | ||
| 3 | return @fromBackingInt(x); | ||
| 4 | } | ||
| 5 | |||
| 6 | const S = packed struct(u32) { x: u32 }; | ||
| 7 | export fn entry2(x: u64) S { | ||
| 8 | return @fromBackingInt(x); | ||
| 9 | } | ||
| 10 | |||
| 11 | const U = packed union(u32) { x: u32 }; | ||
| 12 | export 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 @@ | ||
| 1 | const E = enum(u32) { x }; | ||
| 2 | export fn entry1() void { | ||
| 3 | @compileLog(@as(E, @fromBackingInt(undefined))); | ||
| 4 | } | ||
| 5 | |||
| 6 | const S = packed struct(u32) { x: u32 }; | ||
| 7 | export fn entry2() void { | ||
| 8 | @compileLog(@as(S, @fromBackingInt(undefined))); | ||
| 9 | } | ||
| 10 | |||
| 11 | const U = packed union(u32) { x: u32 }; | ||
| 12 | export fn entry3() void { | ||
| 13 | @compileLog(@as(U, @fromBackingInt(undefined))); | ||
| 14 | } | ||
| 15 | |||
| 16 | // error | ||
| 17 | // | ||
| 18 | // :3:40: error: use of undefined value here causes illegal behavior | ||
| 19 | // | ||
| 20 | // Compile Log Output: | ||
| 21 | // @as(tmp.S, undefined) | ||
| 22 | // @as(tmp.U, undefined) | ||
test/cases/compile_errors/initialize_empty_union.zig+2-2| ... | @@ -1,10 +1,10 @@ | ... | @@ -1,10 +1,10 @@ |
| 1 | const EnumInferred = enum {}; | 1 | const EnumInferred = enum {}; |
| 2 | const EnumExplicit = enum(u8) {}; | 2 | const EnumExplicit = enum(noreturn) {}; |
| 3 | const EnumNonexhaustive = enum(u8) { _ }; | 3 | const EnumNonexhaustive = enum(u8) { _ }; |
| 4 | 4 | ||
| 5 | const U0 = union {}; | 5 | const U0 = union {}; |
| 6 | const U1 = union(enum) {}; | 6 | const U1 = union(enum) {}; |
| 7 | const U2 = union(enum(u8)) {}; | 7 | const U2 = union(enum(noreturn)) {}; |
| 8 | const U3 = union(EnumInferred) {}; | 8 | const U3 = union(EnumInferred) {}; |
| 9 | const U4 = union(EnumExplicit) {}; | 9 | const U4 = union(EnumExplicit) {}; |
| 10 | const U5 = union(EnumNonexhaustive) {}; | 10 | const U5 = union(EnumNonexhaustive) {}; |
test/cases/compile_errors/instantiate_empty_enum.zig created+32| ... | @@ -0,0 +1,32 @@ | ||
| 1 | const E = enum {}; | ||
| 2 | |||
| 3 | export fn entry1() void { | ||
| 4 | const e: E = undefined; | ||
| 5 | _ = e; | ||
| 6 | } | ||
| 7 | |||
| 8 | export fn entry2() void { | ||
| 9 | const e: E = @enumFromInt(@as(u8, undefined)); | ||
| 10 | _ = e; | ||
| 11 | } | ||
| 12 | |||
| 13 | export fn entry3() void { | ||
| 14 | const e: E = .a; | ||
| 15 | _ = e; | ||
| 16 | } | ||
| 17 | |||
| 18 | export fn entry4() void { | ||
| 19 | const e: E = @enumFromInt(0); | ||
| 20 | _ = e; | ||
| 21 | } | ||
| 22 | |||
| 23 | // error | ||
| 24 | // | ||
| 25 | // :4:18: error: expected type 'tmp.E', found '@TypeOf(undefined)' | ||
| 26 | // :4:18: note: cannot coerce to uninstantiable type 'tmp.E' | ||
| 27 | // :1:11: note: enum declared here | ||
| 28 | // :9:31: error: use of undefined value here causes illegal behavior | ||
| 29 | // :14:19: error: enum 'tmp.E' has no member named 'a' | ||
| 30 | // :1:11: note: enum declared here | ||
| 31 | // :19:18: error: enum 'tmp.E' has no tag with value '0' | ||
| 32 | // :1:11: note: enum declared here | ||
test/cases/compile_errors/int_from_enum_undefined.zig deleted-11| ... | @@ -1,11 +0,0 @@ | ||
| 1 | export fn a() void { | ||
| 2 | const E = enum {}; | ||
| 3 | var e: E = undefined; | ||
| 4 | _ = &e; | ||
| 5 | _ = @intFromEnum(e); | ||
| 6 | } | ||
| 7 | |||
| 8 | // error | ||
| 9 | // | ||
| 10 | // :5:22: error: cannot use @intFromEnum on empty enum 'tmp.a.E' | ||
| 11 | // :2:15: note: enum declared here | ||
test/cases/compile_errors/invalid_non-exhaustive_enum_to_union.zig+1-1| ... | @@ -22,5 +22,5 @@ export fn bar() void { | ... | @@ -22,5 +22,5 @@ export fn bar() void { |
| 22 | // | 22 | // |
| 23 | // :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum | 23 | // :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum |
| 24 | // :1:11: note: enum declared here | 24 | // :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 here | 26 | // :6:11: note: union declared here |
test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig+8| ... | @@ -81,6 +81,12 @@ export fn entry15() void { | ... | @@ -81,6 +81,12 @@ export fn entry15() void { |
| 81 | x: *const u32, | 81 | x: *const u32, |
| 82 | }); | 82 | }); |
| 83 | } | 83 | } |
| 84 | export fn entry16() void { | ||
| 85 | const E = enum(noreturn) {}; | ||
| 86 | _ = @sizeOf(packed struct { | ||
| 87 | x: E, | ||
| 88 | }); | ||
| 89 | } | ||
| 84 | 90 | ||
| 85 | // error | 91 | // error |
| 86 | // | 92 | // |
| ... | @@ -114,3 +120,5 @@ export fn entry15() void { | ... | @@ -114,3 +120,5 @@ export fn entry15() void { |
| 114 | // :81:12: error: packed structs cannot contain fields of type '*const u32' | 120 | // :81:12: error: packed structs cannot contain fields of type '*const u32' |
| 115 | // :81:12: note: pointers cannot be directly bitpacked | 121 | // :81:12: note: pointers cannot be directly bitpacked |
| 116 | // :81:12: note: consider using 'usize' and '@intFromPtr' | 122 | // :81:12: note: consider using 'usize' and '@intFromPtr' |
| 123 | // :87:12: error: packed structs cannot contain fields of type 'tmp.entry16.E' | ||
| 124 | // :87:12: note: type does not have a bit-packed representation |
test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig+8| ... | @@ -10,6 +10,12 @@ export fn entry1() void { | ... | @@ -10,6 +10,12 @@ export fn entry1() void { |
| 10 | x: *const u32, | 10 | x: *const u32, |
| 11 | }); | 11 | }); |
| 12 | } | 12 | } |
| 13 | export fn entry2() void { | ||
| 14 | const E = enum(noreturn) {}; | ||
| 15 | _ = @sizeOf(packed union { | ||
| 16 | x: E, | ||
| 17 | }); | ||
| 18 | } | ||
| 13 | 19 | ||
| 14 | // error | 20 | // error |
| 15 | // | 21 | // |
| ... | @@ -19,3 +25,5 @@ export fn entry1() void { | ... | @@ -19,3 +25,5 @@ export fn entry1() void { |
| 19 | // :10:12: error: packed unions cannot contain fields of type '*const u32' | 25 | // :10:12: error: packed unions cannot contain fields of type '*const u32' |
| 20 | // :10:12: note: pointers cannot be directly bitpacked | 26 | // :10:12: note: pointers cannot be directly bitpacked |
| 21 | // :10:12: note: consider using 'usize' and '@intFromPtr' | 27 | // :10:12: note: consider using 'usize' and '@intFromPtr' |
| 28 | // :16:12: error: packed unions cannot contain fields of type 'tmp.entry2.E' | ||
| 29 | // :16:12: note: type does not have a bit-packed representation |
test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const Tag = @Enum(u0, .exhaustive, &.{}, &.{}); | 1 | const Tag = @Enum(noreturn, .exhaustive, &.{}, &.{}); |
| 2 | const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{})); | 2 | const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{})); |
| 3 | export fn entry() void { | 3 | export fn entry() void { |
| 4 | const tagged: Tagged = undefined; | 4 | const tagged: Tagged = undefined; |
test/cases/compile_errors/sizeof_alignof_empty_union.zig+2-2| ... | @@ -1,10 +1,10 @@ | ... | @@ -1,10 +1,10 @@ |
| 1 | const EnumInferred = enum {}; | 1 | const EnumInferred = enum {}; |
| 2 | const EnumExplicit = enum(u8) {}; | 2 | const EnumExplicit = enum(noreturn) {}; |
| 3 | const EnumNonexhaustive = enum(u8) { _ }; | 3 | const EnumNonexhaustive = enum(u8) { _ }; |
| 4 | 4 | ||
| 5 | const U0 = union {}; | 5 | const U0 = union {}; |
| 6 | const U1 = union(enum) {}; | 6 | const U1 = union(enum) {}; |
| 7 | const U2 = union(enum(u8)) {}; | 7 | const U2 = union(enum(noreturn)) {}; |
| 8 | const U3 = union(EnumInferred) {}; | 8 | const U3 = union(EnumInferred) {}; |
| 9 | const U4 = union(EnumExplicit) {}; | 9 | const U4 = union(EnumExplicit) {}; |
| 10 | const U5 = union(EnumNonexhaustive) {}; | 10 | const U5 = union(EnumNonexhaustive) {}; |
test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig+1-1| ... | @@ -6,5 +6,5 @@ test "enum" { | ... | @@ -6,5 +6,5 @@ test "enum" { |
| 6 | // error | 6 | // error |
| 7 | // is_test=true | 7 | // 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 here | 10 | // :2:15: note: declared here |
test/cases/safety/backing_int_no_matching_tag_value.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 | } | ||
| 10 | const Foo = enum(u8) { | ||
| 11 | a, | ||
| 12 | b, | ||
| 13 | c, | ||
| 14 | }; | ||
| 15 | pub fn main() !void { | ||
| 16 | _ = bar(3); | ||
| 17 | return error.TestFailed; | ||
| 18 | } | ||
| 19 | fn 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 | } | ||
| 10 | const Foo = enum(u8) { | ||
| 11 | a, | ||
| 12 | b, | ||
| 13 | c, | ||
| 14 | }; | ||
| 15 | pub fn main() !void { | ||
| 16 | _ = bar(3); | ||
| 17 | return error.TestFailed; | ||
| 18 | } | ||
| 19 | fn bar(a: u8) Foo { | ||
| 20 | return @bitCast(a); | ||
| 21 | } | ||
| 22 | |||
| 23 | // run | ||
| 24 | // backend=selfhosted,llvm | ||
| 25 | // target=x86_64-linux,aarch64-linux | ||