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

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

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

60 files changed, 1578 insertions(+), 255 deletions(-)

doc/langref.html.in+62-14
......@@ -2271,6 +2271,7 @@ or
22712271 This even works at {#link|comptime#}:
22722272 </p>
22732273 {#code|test_packed_structs.zig#}
2274
22742275 <p>
22752276 The backing integer can be inferred or explicitly provided. When
22762277 inferred, it will be unsigned. When explicitly provided, its bit width
......@@ -2279,6 +2280,12 @@ or
22792280 </p>
22802281 {#code|test_missized_packed_struct.zig#}
22812282
2283 <p>
2284 A {#syntax#}packed struct{#endsyntax#} can be converted to and from its backing
2285 integer using {#link|@backingInt#} and {#link|@fromBackingInt#}:
2286 </p>
2287 {#code|test_packed_struct_backing_int.zig#}
2288
22822289 <p>
22832290 Zig allows the address to be taken of a non-byte-aligned field:
22842291 </p>
......@@ -2401,7 +2408,7 @@ or
24012408 {#header_open|enum#}
24022409 {#code|test_enums.zig#}
24032410
2404 {#see_also|@typeInfo|@tagName|@sizeOf|noreturn#}
2411 {#see_also|@backingInt|@fromBackingInt|@typeInfo|@tagName|@sizeOf|noreturn#}
24052412
24062413 {#header_open|extern enum#}
24072414 <p>
......@@ -2431,9 +2438,7 @@ or
24312438 The enum must specify a tag type and cannot consume every enumeration value.
24322439 </p>
24332440 <p>
2434 {#link|@enumFromInt#} on a non-exhaustive enum involves the safety semantics
2435 of {#link|@intCast#} to the integer tag type, but beyond that always results in
2436 a well-defined enum value.
2441 {#link|@fromBackingInt#} on a non-exhaustive enum always results in a valid enum value.
24372442 </p>
24382443 <p>
24392444 A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.
......@@ -2461,7 +2466,8 @@ or
24612466 {#code|test_simple_union.zig#}
24622467
24632468 <p>
2464 In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
2469 In order to use {#link|switch#} with a union, it must be a {#link|tagged union|Tagged union#}
2470 or a {#link|packed union#}.
24652471 </p>
24662472 <p>
24672473 To initialize a union when the tag is a {#link|comptime#}-known name, see {#link|@unionInit#}.
......@@ -2490,7 +2496,7 @@ or
24902496 <p>
24912497 Unions with inferred enum tag types can also assign ordinal values to their inferred tag.
24922498 This requires the tag to specify an explicit integer type.
2493 {#link|@intFromEnum#} can be used to access the ordinal value corresponding to the active field.
2499 {#link|@backingInt#} can be used to access the ordinal value corresponding to the active field.
24942500 </p>
24952501 {#code|test_tagged_union_with_tag_values.zig#}
24962502
......@@ -2521,6 +2527,7 @@ or
25212527 </p>
25222528 {#code|test_packed_union_equality.zig#}
25232529
2530 {#see_also|@backingInt|@fromBackingInt#}
25242531 {#header_close#}
25252532
25262533 {#header_open|Anonymous Union Literals#}
......@@ -3586,14 +3593,14 @@ void do_a_thing(struct Foo *foo) {
35863593 <ul>
35873594 <li>{#link|@bitCast#} - change type but maintain bit representation</li>
35883595 <li>{#link|@alignCast#} - make a pointer have more alignment</li>
3589 <li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>
3596 <li>{#link|@fromBackingInt#} - obtain an enum or a packed struct/union value based on its backing integer</li>
35903597 <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>
35913598 <li>{#link|@errorCast#} - convert to a smaller error set</li>
35923599 <li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
35933600 <li>{#link|@floatFromInt#} - convert an integer to a float value</li>
35943601 <li>{#link|@intCast#} - convert between integer types</li>
35953602 <li>{#link|@intFromBool#} - convert true to 1 and false to 0</li>
3596 <li>{#link|@intFromEnum#} - obtain the integer tag value of an enum or tagged union</li>
3603 <li>{#link|@backingInt#} - obtain the backing integer value of an enum or a packed struct/union</li>
35973604 <li>{#link|@intFromError#} - obtain the integer value of an error code</li>
35983605 <li>{#link|@round#}, {#link|@floor#}, {#link|@ceil#}, {#link|@trunc#} - float to integer conversion</li>
35993606 <li>{#link|@intFromPtr#} - obtain the address of a pointer</li>
......@@ -4432,6 +4439,18 @@ comptime {
44324439 {#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
44334440 {#header_close#}
44344441
4442 {#header_open|@backingInt#}
4443 <pre>{#syntax#}@backingInt(enum_or_bitpack: T) BackingInt(T){#endsyntax#}</pre>
4444 <p>
4445 Converts an {#link|enum#}, a {#link|packed struct#} or a {#link|packed union#} value
4446 to its backing integer.
4447 </p>
4448 <p>
4449 Also works with {#link|tagged unions|Tagged union#}, acting on the active enum tag value.
4450 </p>
4451 {#see_also|@fromBackingInt|@bitCast#}
4452 {#header_close#}
4453
44354454 {#header_open|@bitCast#}
44364455 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
44374456 <p>
......@@ -4452,8 +4471,18 @@ comptime {
44524471 <li>Convert {#syntax#}i32{#endsyntax#} to {#syntax#}u32{#endsyntax#} preserving twos complement</li>
44534472 </ul>
44544473 <p>
4455 Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time. It's a compile error to bitcast a value of undefined layout; this means that, besides the restriction from types which possess dedicated casting builtins (enums, pointers, error sets), bare structs, error unions, slices, optionals, and any other type without a well-defined memory layout, also cannot be used in this operation.
4474 Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time.
4475 It's a compile error to bitcast a value of undefined layout; this means that,
4476 besides the restriction from types which possess dedicated casting builtins
4477 (pointers, error sets), bare structs, error unions, slices, optionals, and any
4478 other type without a well-defined memory layout, also cannot be used in this
4479 operation.
4480 </p>
4481 <p>
4482 Attempting to convert an integer with no corresponding tag value to an
4483 {#syntax#}enum{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}.
44564484 </p>
4485 {#see_also|@ptrCast|@intFromPtr|@ptrFromInt|@errorCast|@intFromError|@errorFromInt|@backingInt|@fromBackingInt#}
44574486 {#header_close#}
44584487
44594488 {#header_open|@bitOffsetOf#}
......@@ -4798,6 +4827,9 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
47984827 {#header_open|@enumFromInt#}
47994828 <pre>{#syntax#}@enumFromInt(integer: anytype) anytype{#endsyntax#}</pre>
48004829 <p>
4830 Deprecated. Use {#link|@fromBackingInt#} or {#link|@bitCast#} instead.
4831 </p>
4832 <p>
48014833 Converts an integer into an {#link|enum#} value. The return type is the inferred result type.
48024834 </p>
48034835 <p>
......@@ -4953,6 +4985,22 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
49534985 </p>
49544986 {#header_close#}
49554987
4988 {#header_open|@fromBackingInt#}
4989 <pre>{#syntax#}@fromBackingInt(backing_int: BackingInt(T)) T{#endsyntax#}</pre>
4990 <p>
4991 Converts an integer into a {#link|enum#}, a {#link|packed struct#} or a
4992 {#link|packed union#} value. The return type is the inferred result type.
4993 </p>
4994 <p>
4995 Attempting to convert an integer with no corresponding tag value to an
4996 {#syntax#}enum{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}.
4997 Note that a {#link|non-exhaustive enum|Non-exhaustive enum#} has corresponding values for
4998 all integers in the enum's integer tag type: the {#syntax#}_{#endsyntax#} value represents
4999 all the remaining unnamed integers in the enum's tag type.
5000 </p>
5001 {#see_also|@backingInt|@bitCast#}
5002 {#header_close#}
5003
49565004 {#header_open|@hasDecl#}
49575005 <pre>{#syntax#}@hasDecl(comptime Namespace: type, comptime name: []const u8) bool{#endsyntax#}</pre>
49585006 <p>Returns whether or not a {#link|Namespace#} has a declaration matching {#syntax#}name{#endsyntax#}.</p>
......@@ -5035,12 +5083,11 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50355083 {#header_open|@intFromEnum#}
50365084 <pre>{#syntax#}@intFromEnum(enum_or_tagged_union: anytype) anytype{#endsyntax#}</pre>
50375085 <p>
5038 Converts an enumeration value into its integer tag type. When a tagged union is passed,
5039 the tag value is used as the enumeration value.
5086 Deprecated. Use {#link|@backingInt#} or {#link|@bitCast#} instead.
50405087 </p>
50415088 <p>
5042 If there is only one possible enum value, the result is a {#syntax#}comptime_int{#endsyntax#}
5043 known at {#link|comptime#}.
5089 Converts an enumeration value into its integer tag type. When a tagged union is passed,
5090 the tag value is used as the enumeration value.
50445091 </p>
50455092 {#see_also|@enumFromInt#}
50465093 {#header_close#}
......@@ -5077,7 +5124,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50775124 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer.
50785125 {#syntax#}value{#endsyntax#} can be {#syntax#}*T{#endsyntax#} or {#syntax#}?*T{#endsyntax#}.
50795126 </p>
5080 <p>To convert the other way, use {#link|@ptrFromInt#}</p>
5127 {#see_also|@ptrFromInt#}
50815128 {#header_close#}
50825129
50835130 {#header_open|@max#}
......@@ -5281,6 +5328,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52815328 If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}
52825329 is zero, this invokes safety-checked {#link|Illegal Behavior#}.
52835330 </p>
5331 {#see_also|@intFromPtr#}
52845332 {#header_close#}
52855333
52865334 {#header_open|@rem#}
doc/langref/runtime_invalid_enum_cast.zig+7-5
......@@ -1,16 +1,18 @@
11const std = @import("std");
22
3const Foo = enum {
3const Foo = enum(u2) {
44 a,
55 b,
66 c,
77};
88
9pub fn main() void {
10 var a: u2 = 3;
11 _ = &a;
12 const b: Foo = @enumFromInt(a);
9fn foo(a: u2) void {
10 const b: Foo = @fromBackingInt(a);
1311 std.debug.print("value: {s}\n", .{@tagName(b)});
1412}
1513
14pub fn main() void {
15 foo(3);
16}
17
1618// exe=fail
doc/langref/test_comptime_invalid_enum_cast.zig+2-2
......@@ -1,11 +1,11 @@
1const Foo = enum {
1const Foo = enum(u2) {
22 a,
33 b,
44 c,
55};
66comptime {
77 const a: u2 = 3;
8 const b: Foo = @enumFromInt(a);
8 const b: Foo = @fromBackingInt(a);
99 _ = b;
1010}
1111
doc/langref/test_enums.zig+11-11
......@@ -22,9 +22,9 @@ const Value = enum(u2) {
2222// Now you can cast between u2 and Value.
2323// The ordinal value starts from 0, counting up by 1 from the previous member.
2424test "enum ordinal value" {
25 try expectEqual(0, @intFromEnum(Value.zero));
26 try expectEqual(1, @intFromEnum(Value.one));
27 try expectEqual(2, @intFromEnum(Value.two));
25 try expectEqual(0, @backingInt(Value.zero));
26 try expectEqual(1, @backingInt(Value.one));
27 try expectEqual(2, @backingInt(Value.two));
2828}
2929
3030// You can override the ordinal value for an enum.
......@@ -34,9 +34,9 @@ const Value2 = enum(u32) {
3434 million = 1000000,
3535};
3636test "set enum ordinal value" {
37 try expectEqual(100, @intFromEnum(Value2.hundred));
38 try expectEqual(1000, @intFromEnum(Value2.thousand));
39 try expectEqual(1000000, @intFromEnum(Value2.million));
37 try expectEqual(100, @backingInt(Value2.hundred));
38 try expectEqual(1000, @backingInt(Value2.thousand));
39 try expectEqual(1000000, @backingInt(Value2.million));
4040}
4141
4242// You can also override only some values.
......@@ -48,11 +48,11 @@ const Value3 = enum(u4) {
4848 e,
4949};
5050test "enum implicit ordinal values and overridden values" {
51 try expectEqual(0, @intFromEnum(Value3.a));
52 try expectEqual(8, @intFromEnum(Value3.b));
53 try expectEqual(9, @intFromEnum(Value3.c));
54 try expectEqual(4, @intFromEnum(Value3.d));
55 try expectEqual(5, @intFromEnum(Value3.e));
51 try expectEqual(0, @backingInt(Value3.a));
52 try expectEqual(8, @backingInt(Value3.b));
53 try expectEqual(9, @backingInt(Value3.c));
54 try expectEqual(4, @backingInt(Value3.d));
55 try expectEqual(5, @backingInt(Value3.e));
5656}
5757
5858// Enums can have methods, the same as structs and unions.
doc/langref/test_inline_else.zig+2-2
......@@ -9,7 +9,7 @@ const SliceTypeB = extern struct {
99 ptr: [*]SliceTypeA,
1010 len: usize,
1111};
12const AnySlice = union(enum) {
12const AnySlice = union(enum(u8)) {
1313 a: SliceTypeA,
1414 b: SliceTypeB,
1515 c: []const u8,
......@@ -23,7 +23,7 @@ fn withFor(any: AnySlice) usize {
2323 // With `inline for` the function gets generated as
2424 // a series of `if` statements relying on the optimizer
2525 // to convert it to a switch.
26 if (field_value == @intFromEnum(any)) {
26 if (field_value == @backingInt(any)) {
2727 return @field(any, field_name).len;
2828 }
2929 }
doc/langref/test_packed_struct_backing_int.zig created+21
......@@ -0,0 +1,21 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const expectEqual = std.testing.expectEqual;
4
5const PackedStruct = packed struct(u8) {
6 lo: u4,
7 hi: u4,
8};
9
10test "convert to and from backing integer" {
11 const original: PackedStruct = .{ .lo = 0b1100, .hi = 0b0101 };
12
13 const backing_int = @backingInt(original);
14 comptime assert(@TypeOf(backing_int) == u8);
15 try expectEqual(0b0101_1100, backing_int);
16
17 const reconstructed: PackedStruct = @fromBackingInt(backing_int);
18 try expectEqual(original, reconstructed);
19}
20
21// test
doc/langref/test_tagged_union_with_tag_values.zig+2-2
......@@ -8,10 +8,10 @@ const Tagged = union(enum(u32)) {
88
99test "tag values" {
1010 const int: Tagged = .{ .int = -40 };
11 try expectEqual(123, @intFromEnum(int));
11 try expectEqual(123, @backingInt(int));
1212
1313 const boolean: Tagged = .{ .boolean = false };
14 try expectEqual(67, @intFromEnum(boolean));
14 try expectEqual(67, @backingInt(boolean));
1515}
1616
1717// test
lib/std/meta.zig+31-1
......@@ -506,10 +506,40 @@ pub fn BareUnion(comptime T: type) type {
506506 .@"union" => |u| u,
507507 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),
508508 };
509
510509 return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);
511510}
512511
512/// For enums, packed unions and packed structs, returns their backing integer type.
513/// For tagged unions, returns the backing integer type of their enum tag type.
514pub fn BackingInt(comptime T: type) type {
515 switch (@typeInfo(T)) {
516 .@"enum" => |info| return info.tag_type,
517 .@"struct" => |info| if (info.backing_integer) |Int| return Int,
518 .@"union" => |info| switch (info.layout) {
519 .@"packed" => return info.backing_integer.?,
520 .auto => if (info.tag_type) |EnumTag|
521 return @typeInfo(EnumTag).@"enum".tag_type,
522 .@"extern" => {},
523 },
524 else => {},
525 }
526 @compileError("expected enum, tagged union, packed union or packed struct type, found '" ++ @typeName(T) ++ "'");
527}
528
529test BackingInt {
530 const E = enum(u8) { a, b, c };
531 try testing.expect(BackingInt(E) == u8);
532
533 const S = packed struct(u16) { x: u8, y: i8 };
534 try testing.expect(BackingInt(S) == u16);
535
536 const U = packed union(i32) { a: u32, b: enum(i32) { _ } };
537 try testing.expect(BackingInt(U) == i32);
538
539 const T = union(enum(i8)) { a, b, c };
540 try testing.expect(BackingInt(T) == i8);
541}
542
513543pub fn Tag(comptime T: type) type {
514544 return switch (@typeInfo(T)) {
515545 .@"enum" => |info| info.tag_type,
lib/std/zig/AstGen.zig+18
......@@ -2703,6 +2703,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
27032703 .elem_type,
27042704 .indexable_ptr_elem_type,
27052705 .splat_op_result_ty,
2706 .from_backing_int_arg_ty,
27062707 .reify_int,
27072708 .vector_type,
27082709 .indexable_ptr_len,
......@@ -2803,6 +2804,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28032804 .error_set_decl,
28042805 .enum_from_int,
28052806 .int_from_enum,
2807 .backing_int,
2808 .from_backing_int,
28062809 .type_info,
28072810 .size_of,
28082811 .bit_size_of,
......@@ -9168,6 +9171,7 @@ fn builtinCall(
91689171 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
91699172 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
91709173 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),
9174 .backing_int => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .backing_int),
91719175 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
91729176 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
91739177 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
......@@ -9199,6 +9203,20 @@ fn builtinCall(
91999203 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
92009204 // zig fmt: on
92019205
9206 .from_backing_int => {
9207 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9208 const result_ty = try ri.rl.resultTypeForCast(gz, node, builtin_name);
9209 const backing_int_ty = try gz.addUnNode(.from_backing_int_arg_ty, result_ty, node);
9210 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = backing_int_ty } }, params[0]);
9211
9212 try emitDbgStmt(gz, cursor);
9213 const result = try gz.addPlNode(.from_backing_int, node, Zir.Inst.Bin{
9214 .lhs = result_ty,
9215 .rhs = operand,
9216 });
9217 return rvalue(gz, ri, result, node);
9218 },
9219
92029220 .in_comptime => if (gz.is_comptime) {
92039221 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
92049222 } else {
lib/std/zig/AstRlAnnotate.zig+2
......@@ -889,6 +889,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
889889 .int_from_bool,
890890 .int_from_error,
891891 .error_from_int,
892 .from_backing_int,
892893 .embed_file,
893894 .error_name,
894895 .set_runtime_safety,
......@@ -916,6 +917,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
916917 .float_from_int,
917918 .ptr_from_int,
918919 .enum_from_int,
920 .backing_int,
919921 .float_cast,
920922 .int_cast,
921923 .truncate,
lib/std/zig/BuiltinFn.zig+16
......@@ -56,6 +56,8 @@ pub const Tag = enum {
5656 import,
5757 in_comptime,
5858 int_cast,
59 backing_int,
60 from_backing_int,
5961 enum_from_int,
6062 error_from_int,
6163 float_from_int,
......@@ -564,6 +566,20 @@ pub const list = list: {
564566 .param_count = 1,
565567 },
566568 },
569 .{
570 "@backingInt",
571 .{
572 .tag = .backing_int,
573 .param_count = 1,
574 },
575 },
576 .{
577 "@fromBackingInt",
578 .{
579 .tag = .from_backing_int,
580 .param_count = 1,
581 },
582 },
567583 .{
568584 "@enumFromInt",
569585 .{
lib/std/zig/Zir.zig+28-2
......@@ -283,6 +283,14 @@ pub const Inst = struct {
283283 ///
284284 /// Uses the `un_node` field.
285285 splat_op_result_ty,
286 /// Given a type, strips away any error unions or optionals stacked
287 /// on top, validates it for usage with `@fromBackingInt` and returns
288 /// its backing integer type.
289 ///
290 /// `E!?enum(T) { _ }` -> `T`
291 ///
292 /// Uses the `un_node` field.
293 from_backing_int_arg_ty,
286294 /// Given a pointer to an indexable object, returns the len property. This is
287295 /// used by for loops. This instruction also emits a for-loop specific compile
288296 /// error if the indexable object is not indexable.
......@@ -872,6 +880,9 @@ pub const Inst = struct {
872880 /// Converts an enum value into an integer. Resulting type will be the tag type
873881 /// of the enum. Uses `un_node`.
874882 int_from_enum,
883 /// Implements the `@backingInt` builtin.
884 /// Uses `un_node`.
885 backing_int,
875886 /// Implement builtin `@alignOf`. Uses `un_node`.
876887 align_of,
877888 /// Implement builtin `@intFromBool`. Uses `un_node`.
......@@ -934,6 +945,9 @@ pub const Inst = struct {
934945 /// Converts an integer into an enum value.
935946 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
936947 enum_from_int,
948 /// Implements the `@fromBackingInt` builtin.
949 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
950 from_backing_int,
937951 /// Convert a larger float type to any other float type, possibly causing
938952 /// a loss of precision.
939953 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
......@@ -1115,6 +1129,7 @@ pub const Inst = struct {
11151129 .elem_type,
11161130 .indexable_ptr_elem_type,
11171131 .splat_op_result_ty,
1132 .from_backing_int_arg_ty,
11181133 .indexable_ptr_len,
11191134 .anyframe_type,
11201135 .as_node,
......@@ -1229,6 +1244,8 @@ pub const Inst = struct {
12291244 .field_type_ref,
12301245 .enum_from_int,
12311246 .int_from_enum,
1247 .backing_int,
1248 .from_backing_int,
12321249 .type_info,
12331250 .size_of,
12341251 .bit_size_of,
......@@ -1409,6 +1426,7 @@ pub const Inst = struct {
14091426 .elem_type,
14101427 .indexable_ptr_elem_type,
14111428 .splat_op_result_ty,
1429 .from_backing_int_arg_ty,
14121430 .indexable_ptr_len,
14131431 .anyframe_type,
14141432 .as_node,
......@@ -1511,6 +1529,8 @@ pub const Inst = struct {
15111529 .field_type_ref,
15121530 .enum_from_int,
15131531 .int_from_enum,
1532 .backing_int,
1533 .from_backing_int,
15141534 .type_info,
15151535 .size_of,
15161536 .bit_size_of,
......@@ -1645,6 +1665,7 @@ pub const Inst = struct {
16451665 .elem_type = .un_node,
16461666 .indexable_ptr_elem_type = .un_node,
16471667 .splat_op_result_ty = .un_node,
1668 .from_backing_int_arg_ty = .un_node,
16481669 .indexable_ptr_len = .un_node,
16491670 .anyframe_type = .un_node,
16501671 .as_node = .pl_node,
......@@ -1774,6 +1795,7 @@ pub const Inst = struct {
17741795 .compile_error = .un_node,
17751796 .set_eval_branch_quota = .un_node,
17761797 .int_from_enum = .un_node,
1798 .backing_int = .un_node,
17771799 .align_of = .un_node,
17781800 .int_from_bool = .un_node,
17791801 .embed_file = .un_node,
......@@ -1803,6 +1825,7 @@ pub const Inst = struct {
18031825 .float_from_int = .pl_node,
18041826 .ptr_from_int = .pl_node,
18051827 .enum_from_int = .pl_node,
1828 .from_backing_int = .pl_node,
18061829 .float_cast = .pl_node,
18071830 .int_cast = .pl_node,
18081831 .ptr_cast = .pl_node,
......@@ -2160,8 +2183,8 @@ pub const Inst = struct {
21602183 astgen_error,
21612184 /// Given a type, strips away any error unions or optionals stacked
21622185 /// on top and returns the base type. That base type must be a float.
2163 /// For example: Provided with error{Foo}!?f64, returns f64.
2164 /// `operand` is `operand: Air.Inst.Ref`.
2186 /// For example: Provided with `error{Foo}!?f64`, returns `f64`.
2187 /// `operand` is payload index to `UnNode`.
21652188 float_op_result_ty,
21662189
21672190 pub const InstData = struct {
......@@ -4141,6 +4164,7 @@ fn findTrackableInner(
41414164 .elem_type,
41424165 .indexable_ptr_elem_type,
41434166 .splat_op_result_ty,
4167 .from_backing_int_arg_ty,
41444168 .indexable_ptr_len,
41454169 .anyframe_type,
41464170 .as_node,
......@@ -4296,6 +4320,8 @@ fn findTrackableInner(
42964320 .float_from_int,
42974321 .ptr_from_int,
42984322 .enum_from_int,
4323 .backing_int,
4324 .from_backing_int,
42994325 .float_cast,
43004326 .int_cast,
43014327 .ptr_cast,
src/Air.zig+6
......@@ -288,6 +288,10 @@ pub const Inst = struct {
288288 ///
289289 /// Uses the `ty_op` field.
290290 bit_cast,
291 /// Like `bit_cast`, but triggers a safety panic if the destination type is an exhaustive
292 /// enum and the operand is not a valid value of this type;
293 /// i.e. equivalent to a safety check based on `.is_named_enum_value`
294 bit_cast_safe,
291295 /// Cast a pointer to a different pointer type. The result type is a slice iff the operand
292296 /// type is a slice (the length of the slice does not change). All other pointer attributes
293297 /// except for the address space may change.
......@@ -1717,6 +1721,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
17171721
17181722 .not,
17191723 .bit_cast,
1724 .bit_cast_safe,
17201725 .ptr_cast,
17211726 .ptr_from_int,
17221727 .int_from_ptr,
......@@ -1969,6 +1974,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
19691974 .add_safe,
19701975 .sub_safe,
19711976 .mul_safe,
1977 .bit_cast_safe,
19721978 .int_cast_safe,
19731979 .int_from_float_safe,
19741980 .int_from_float_optimized_safe,
src/Air/Legalize.zig+110-4
......@@ -156,6 +156,10 @@ pub const Feature = enum {
156156 /// Legalize splat to a one element vector to a bitcast.
157157 splat_one_elem_to_bit_cast,
158158
159 /// Replace `bit_cast_safe` with an explicit safety check which `call`s the panic function on failure.
160 /// `scalarize_*` variants for `bit_cast_safe` do not exist since the safety check is only desired if the result
161 /// type is a scalar enum type, so the scalarizatins for regular `bit_cast` are exactly equivalent.
162 expand_bit_cast_safe,
159163 /// Replace `int_cast_safe` with an explicit safety check which `call`s the panic function on failure.
160164 /// Not compatible with `scalarize_int_cast_safe`.
161165 expand_int_cast_safe,
......@@ -600,6 +604,17 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
600604 continue :inst l.replaceInst(inst, .block, payload);
601605 }
602606 },
607 .bit_cast_safe => if (l.features.has(.expand_bit_cast_safe)) {
608 continue :inst l.replaceInst(inst, .block, try l.safeBitcastBlockPayload(inst));
609 } else if (l.features.hasAny(&.{
610 .scalarize_bit_cast_array,
611 .scalarize_bit_cast_vector_non_elementwise,
612 .scalarize_bit_cast_padded_elems,
613 })) {
614 if (try l.scalarizeBitcastBlockPayload(inst)) |payload| {
615 continue :inst l.replaceInst(inst, .block, payload);
616 }
617 },
603618 .int_cast_safe => if (l.features.has(.expand_int_cast_safe)) {
604619 assert(!l.features.has(.scalarize_int_cast_safe)); // it doesn't make sense to do both
605620 continue :inst l.replaceInst(inst, .block, try l.safeIntcastBlockPayload(inst));
......@@ -1006,7 +1021,10 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
10061021
10071022 if (result_is_array) {
10081023 // This is only allowed when legalizing an elementwise bitcast.
1009 assert(orig.tag == .bit_cast);
1024 switch (orig.tag) {
1025 .bit_cast, .bit_cast_safe => {},
1026 else => unreachable,
1027 }
10101028 assert(form == .ty_op);
10111029 }
10121030
......@@ -1076,7 +1094,11 @@ fn scalarizeBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, form: Scalariz
10761094 orig_operand,
10771095 index_val,
10781096 ).toRef();
1079 break :elem loop.block.addTyOp(l, orig.tag, res_elem_ty, operand).toRef();
1097 const scalar_tag: Air.Inst.Tag = switch (orig.tag) {
1098 .bit_cast_safe => .bit_cast, // safety check is not supposed to be elementwise
1099 else => orig.tag,
1100 };
1101 break :elem loop.block.addTyOp(l, scalar_tag, res_elem_ty, operand).toRef();
10801102 },
10811103 .bin_op => elem: {
10821104 const orig_bin = orig.data.bin_op;
......@@ -1700,9 +1722,23 @@ fn scalarizeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!?
17001722
17011723 // Now convert `uint_ty` (`uN`) to `dest_ty`.
17021724
1725 // We omit the safety check when casting to an array or a vector since it's
1726 // not supposed to be elementwise.
1727 if (dest_ty.zigTypeTag(zcu) == .@"enum") assert(int_to_dest_ok);
1728
17031729 if (int_to_dest_ok) {
17041730 _ = main_block.stealCapacity(17);
1705 const result = main_block.addBitCast(l, dest_ty, uint_val);
1731 const result = switch (l.air_instructions.items(.tag)[@intFromEnum(orig_inst)]) {
1732 .bit_cast => main_block.addBitCast(l, dest_ty, uint_val),
1733 .bit_cast_safe => main_block.add(l, .{
1734 .tag = .bit_cast_safe,
1735 .data = .{ .ty_op = .{
1736 .ty = .fromType(dest_ty),
1737 .operand = uint_val,
1738 } },
1739 }).toRef(),
1740 else => unreachable,
1741 };
17061742 main_block.addBr(l, orig_inst, result);
17071743 } else if (dest_ty.arrayLenIncludingSentinel(zcu) == 1) {
17081744 _ = main_block.stealCapacity(16);
......@@ -2089,6 +2125,76 @@ fn scalarizeReduceBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index, optimize
20892125 } };
20902126}
20912127
2128fn safeBitcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2129 const pt = l.pt;
2130 const zcu = pt.zcu;
2131 const ty_op = l.air_instructions.items(.data)[@intFromEnum(orig_inst)].ty_op;
2132
2133 const operand_ref = ty_op.operand;
2134 const dest_ty = ty_op.ty.toType();
2135
2136 // The worst case is a bitcast to an exhaustive enum and looks like this:
2137 //
2138 // %x = block({
2139 // %1 = bit_cast(@res_ty, %y)
2140 // %2 = is_named_enum_value(%1)
2141 // %3 = cond_br(%2, {
2142 // %4 = br(%x, %1)
2143 // }, {
2144 // %5 = call(@panic.invalidEnumValue, [])
2145 // %6 = unreach()
2146 // })
2147 // })
2148
2149 var inst_buf: [6]Air.Inst.Index = undefined;
2150 try l.air_instructions.ensureUnusedCapacity(zcu.gpa, inst_buf.len);
2151 var opt_condbr: ?CondBr = null;
2152
2153 var main_block: Block = .init(&inst_buf);
2154 var cur_block: *Block = &main_block;
2155
2156 const cast_inst = cur_block.addBitCast(l, dest_ty, operand_ref);
2157
2158 if (dest_ty.zigTypeTag(zcu) == .@"enum" and
2159 !dest_ty.isNonexhaustiveEnum(zcu) and
2160 zcu.backendSupportsFeature(.is_named_enum_value))
2161 {
2162 // We are building this:
2163 // %1 = is_named_enum_value(%cast_inst)
2164 // %2 = cond_br(%1, {
2165 // <new cursor>
2166 // }, {
2167 // <panic>
2168 // })
2169 const is_named_inst = cur_block.add(l, .{
2170 .tag = .is_named_enum_value,
2171 .data = .{ .un_op = cast_inst },
2172 });
2173 opt_condbr = .init(l, is_named_inst.toRef(), cur_block, .{ .false = .cold });
2174 const condbr = &(opt_condbr.?);
2175 condbr.else_block = .init(cur_block.stealRemainingCapacity());
2176 try condbr.else_block.addPanic(l, .invalid_enum_value);
2177 condbr.then_block = .init(condbr.else_block.stealRemainingCapacity());
2178 cur_block = &condbr.then_block;
2179 }
2180 // Finally, just `br` to our outer `block`.
2181 _ = cur_block.add(l, .{
2182 .tag = .br,
2183 .data = .{ .br = .{
2184 .block_inst = orig_inst,
2185 .operand = cast_inst,
2186 } },
2187 });
2188 // We might not have used all of the instructions; that's intentional.
2189 _ = cur_block.stealRemainingCapacity();
2190
2191 if (opt_condbr) |condbr| try condbr.finish(l);
2192 return .{ .ty_pl = .{
2193 .ty = .fromType(dest_ty),
2194 .payload = try l.addBlockBody(main_block.body()),
2195 } };
2196}
2197
20922198fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
20932199 const pt = l.pt;
20942200 const zcu = pt.zcu;
......@@ -2172,7 +2278,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In
21722278 const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds;
21732279
21742280 if (have_min_check or have_max_check) {
2175 const dest_int_ty = if (dest_is_enum) dest_ty.intTagType(zcu) else dest_ty;
2281 const dest_int_ty = if (dest_is_enum) dest_ty.backingIntType(zcu) else dest_ty;
21762282 const condbr = &condbr_buf[condbr_idx];
21772283 condbr_idx += 1;
21782284 const below_min_inst: Air.Inst.Index = if (have_min_check) inst: {
src/Air/Liveness.zig+1
......@@ -491,6 +491,7 @@ fn analyzeInst(
491491
492492 .not,
493493 .bit_cast,
494 .bit_cast_safe,
494495 .ptr_cast,
495496 .ptr_from_int,
496497 .int_from_ptr,
src/Air/Liveness/Verify.zig+1
......@@ -79,6 +79,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
7979 // unary
8080 .not,
8181 .bit_cast,
82 .bit_cast_safe,
8283 .ptr_cast,
8384 .ptr_from_int,
8485 .int_from_ptr,
src/Air/Verify.zig+1-1
......@@ -118,7 +118,7 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
118118 if (ptr_ty.childType(zcu).toIntern() != verify.ret_ty.toIntern()) return verify.fail("bad return type");
119119 },
120120
121 .bit_cast => {
121 .bit_cast, .bit_cast_safe => {
122122 const ty_op = data[@intFromEnum(inst)].ty_op;
123123 const operand_ty = air.typeOf(ty_op.operand, ip);
124124 const result_ty = ty_op.ty.toType();
src/Air/print.zig+1
......@@ -231,6 +231,7 @@ const Writer = struct {
231231
232232 .not,
233233 .bit_cast,
234 .bit_cast_safe,
234235 .ptr_cast,
235236 .ptr_from_int,
236237 .int_from_ptr,
src/InternPool.zig+1
......@@ -10192,6 +10192,7 @@ pub fn getCoerced(
1019210192 .enum_type => {
1019310193 const enum_type = ip.loadEnumType(new_ty);
1019410194 const index = enum_type.nameIndex(ip, enum_literal).?;
10195 assert(enum_type.int_tag_type != .noreturn_type);
1019510196 return ip.get(gpa, io, tid, .{ .enum_tag = .{
1019610197 .ty = new_ty,
1019710198 .int = if (enum_type.field_values.len != 0)
src/Sema.zig+247-73
......@@ -1191,11 +1191,14 @@ fn analyzeBodyInner(
11911191 .elem_type => try sema.zirElemType(block, inst),
11921192 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
11931193 .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst),
1194 .from_backing_int_arg_ty => try sema.zirFromBackingIntArgTy(block, inst),
11941195 .enum_literal => try sema.zirEnumLiteral(block, inst),
11951196 .decl_literal => try sema.zirDeclLiteral(block, inst, true),
11961197 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
11971198 .int_from_enum => try sema.zirIntFromEnum(block, inst),
11981199 .enum_from_int => try sema.zirEnumFromInt(block, inst),
1200 .backing_int => try sema.zirBackingInt(block, inst),
1201 .from_backing_int => try sema.zirFromBackingInt(block, inst),
11991202 .err_union_code => try sema.zirErrUnionCode(block, inst),
12001203 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
12011204 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst),
......@@ -2428,6 +2431,28 @@ fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src:
24282431 });
24292432}
24302433
2434fn failWithAmbiguousBackingIntType(
2435 sema: *Sema,
2436 block: *Block,
2437 src: LazySrcLoc,
2438 int_backed_ty: Type,
2439 builtin_name: []const u8,
2440) CompileError {
2441 const pt = sema.pt;
2442 const zcu = pt.zcu;
2443 return sema.failWithOwnedErrorMsg(block, msg: {
2444 const msg = try sema.errMsg(src, "{s} is ambiguous for type '{f}'", .{
2445 builtin_name, int_backed_ty.fmt(pt),
2446 });
2447 errdefer msg.destroy(sema.gpa);
2448 try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "backing integer type of {t} is inferred", .{
2449 int_backed_ty.zigTypeTag(zcu),
2450 });
2451 try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "consider explicitly specifying the backing integer type", .{});
2452 break :msg msg;
2453 });
2454}
2455
24312456fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {
24322457 const pt = sema.pt;
24332458 const msg = msg: {
......@@ -4782,7 +4807,7 @@ fn failWithBadUnionFieldAccess(
47824807 return sema.failWithOwnedErrorMsg(block, msg);
47834808}
47844809
4785pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void {
4810pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) Allocator.Error!void {
47864811 const zcu = sema.pt.zcu;
47874812 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
47884813 const category = switch (decl_ty.zigTypeTag(zcu)) {
......@@ -7853,12 +7878,12 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
78537878 },
78547879 };
78557880 const enum_tag_ty = sema.typeOf(enum_tag);
7856 const int_tag_ty = enum_tag_ty.intTagType(zcu);
7881 const int_tag_ty = enum_tag_ty.backingIntType(zcu);
78577882 assert(int_tag_ty.classify(zcu) != .no_possible_value);
78587883
78597884 if (sema.resolveValue(enum_tag)) |enum_tag_val| {
78607885 if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty);
7861 return .fromValue(enum_tag_val.intFromEnum(zcu));
7886 return .fromValue(enum_tag_val.backingInt(zcu));
78627887 }
78637888
78647889 try sema.requireRuntimeBlock(block, src, operand_src);
......@@ -7884,7 +7909,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
78847909
78857910 if (sema.resolveValue(operand)) |int_val| {
78867911 if (dest_ty.isNonexhaustiveEnum(zcu)) {
7887 const int_tag_ty = dest_ty.intTagType(zcu);
7912 const int_tag_ty = dest_ty.backingIntType(zcu);
78887913 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {
78897914 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
78907915 }
......@@ -7907,7 +7932,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
79077932 if (block.wantSafety()) {
79087933 // The operand is runtime-known but the result is comptime-known. In
79097934 // this case we still need a safety check.
7910 const expect_int = try pt.getCoerced(opv.intFromEnum(zcu), operand_ty);
7935 const expect_int = try pt.getCoerced(opv.backingInt(zcu), operand_ty);
79117936 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));
79127937 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
79137938 }
......@@ -9361,7 +9386,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
93619386 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),
93629387 else => {},
93639388 }
9364
93659389 break :msg msg;
93669390 }),
93679391 .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {
......@@ -9396,7 +9420,183 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
93969420 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
93979421 }
93989422
9399 return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node));
9423 operand_ty.assertHasLayout(zcu);
9424 try sema.ensureLayoutResolved(dest_ty, src, .init);
9425
9426 const operand_bits = operand_ty.bitSize(zcu);
9427 const dest_bits = dest_ty.bitSize(zcu);
9428 if (operand_bits != dest_bits) {
9429 return sema.fail(block, src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
9430 dest_ty.fmt(pt),
9431 dest_bits,
9432 operand_ty.fmt(pt),
9433 operand_bits,
9434 });
9435 }
9436
9437 if (sema.resolveValue(operand)) |operand_val| {
9438 const dest_is_exhaustive_enum = dest_ty.zigTypeTag(zcu) == .@"enum" and
9439 !dest_ty.isNonexhaustiveEnum(zcu);
9440 if (dest_is_exhaustive_enum and operand_val.isUndef(zcu)) {
9441 return sema.failWithUseOfUndef(block, operand_src, null);
9442 }
9443
9444 const result_val = try sema.bitCastVal(operand_val, dest_ty);
9445
9446 if (dest_is_exhaustive_enum and
9447 dest_ty.enumTagFieldIndex(result_val, zcu) == null)
9448 {
9449 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
9450 dest_ty.fmt(pt), result_val.backingInt(zcu).fmtValueSema(pt, sema),
9451 });
9452 }
9453
9454 return .fromValue(result_val);
9455 }
9456
9457 try sema.validateRuntimeValue(block, src, operand);
9458
9459 if (block.wantSafety()) {
9460 try sema.preparePanicId(src, .invalid_enum_value);
9461 return block.addTyOp(.bit_cast_safe, dest_ty, operand);
9462 }
9463 return block.addTyOp(.bit_cast, dest_ty, operand);
9464}
9465
9466fn zirBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9467 const pt = sema.pt;
9468 const zcu = pt.zcu;
9469 const gpa = zcu.comp.gpa;
9470 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
9471 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9472
9473 const operand = sema.resolveInst(inst_data.operand);
9474 const operand_ty = sema.typeOf(operand);
9475 operand_ty.assertHasLayout(zcu);
9476 const int_backed_ref: Air.Inst.Ref = ref: switch (operand_ty.zigTypeTag(zcu)) {
9477 .@"enum" => operand,
9478 .@"union" => {
9479 const union_obj = zcu.intern_pool.loadUnionType(operand_ty.toIntern());
9480 if (union_obj.tag_usage == .tagged) break :ref try sema.unionToTag(block, operand);
9481 if (union_obj.layout == .@"packed") break :ref operand;
9482 return sema.failWithOwnedErrorMsg(block, msg: {
9483 const msg = try sema.errMsg(operand_src, "non-packed union '{f}' does not have a backing integer", .{operand_ty.fmt(pt)});
9484 errdefer msg.deinit(gpa);
9485 try sema.errNote(operand_src, msg, "untagged union '{f}' does not have an enum tag with a backing integer", .{operand_ty.fmt(pt)});
9486 try sema.addDeclaredHereNote(msg, operand_ty);
9487 break :msg msg;
9488 });
9489 },
9490 .@"struct" => {
9491 if (operand_ty.containerLayout(zcu) != .@"packed") {
9492 return sema.fail(block, operand_src, "non-packed struct '{f}' does not have a backing integer", .{
9493 operand_ty.fmt(pt),
9494 });
9495 }
9496 break :ref operand;
9497 },
9498 else => {
9499 return sema.fail(block, operand_src, "expected enum, tagged union, packed union or packed struct, found '{f}'", .{
9500 operand_ty.fmt(pt),
9501 });
9502 },
9503 };
9504 const int_backed_ty = sema.typeOf(int_backed_ref);
9505 if (int_backed_ty.backingIntMode(zcu) != .explicit and int_backed_ty.zigTypeTag(zcu) != .@"enum")
9506 return sema.failWithAmbiguousBackingIntType(block, operand_src, int_backed_ty, "@backingInt");
9507 const backing_int_ty = int_backed_ty.backingIntType(zcu);
9508
9509 if (sema.resolveValue(int_backed_ref)) |int_backed_val| {
9510 if (int_backed_val.isUndef(zcu)) return pt.undefRef(backing_int_ty);
9511 return .fromValue(int_backed_val.backingInt(zcu));
9512 }
9513
9514 switch (backing_int_ty.classify(zcu)) {
9515 .partially_comptime, .fully_comptime => unreachable, // does not apply to integers
9516 .no_possible_value => unreachable, // enum also NPV, cannot instantiate NPV types
9517 .one_possible_value => unreachable, // enum or bitpack also OPV, should have been resolve above
9518 .runtime => {},
9519 }
9520
9521 return block.addTyOp(.bit_cast, backing_int_ty, int_backed_ref);
9522}
9523
9524fn zirFromBackingIntArgTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9525 const pt = sema.pt;
9526 const zcu = pt.zcu;
9527 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
9528 const src = block.nodeOffset(inst_data.src_node);
9529
9530 const dest_ty = try sema.resolveDestType(block, src, inst_data.operand, .remove_eu_opt, "@fromBackingInt");
9531 try sema.ensureLayoutResolved(dest_ty, src, .init);
9532 switch (dest_ty.zigTypeTag(zcu)) {
9533 .@"enum" => {},
9534 .@"struct", .@"union" => |type_tag| if (dest_ty.containerLayout(zcu) != .@"packed") {
9535 return sema.fail(block, src, "non-packed {t} '{f}' does not have a backing integer", .{
9536 type_tag, dest_ty.fmt(pt),
9537 });
9538 },
9539 else => {
9540 return sema.fail(block, src, "expected enum, packed union or packed struct, found '{f}'", .{
9541 dest_ty.fmt(pt),
9542 });
9543 },
9544 }
9545 if (dest_ty.backingIntMode(zcu) != .explicit and dest_ty.zigTypeTag(zcu) != .@"enum")
9546 return sema.failWithAmbiguousBackingIntType(block, src, dest_ty, "@fromBackingInt");
9547 return .fromType(dest_ty.backingIntType(zcu));
9548}
9549
9550fn zirFromBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9551 const pt = sema.pt;
9552 const zcu = pt.zcu;
9553 const ip = &zcu.intern_pool;
9554
9555 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
9556 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9557 const src = block.nodeOffset(inst_data.src_node);
9558 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9559
9560 // Type has already been validated and layout-resolved by `zirFromBackingIntArgTy`.
9561 const dest_ty = try sema.resolveDestType(block, .unneeded, extra.lhs, .remove_eu_opt, undefined);
9562 dest_ty.assertHasLayout(zcu);
9563
9564 const operand = sema.resolveInst(extra.rhs);
9565 const backing_int_ref = try sema.coerce(block, dest_ty.backingIntType(zcu), operand, operand_src);
9566
9567 if (sema.resolveValue(backing_int_ref)) |backing_int_val| {
9568 if (dest_ty.zigTypeTag(zcu) != .@"enum") {
9569 // we don't do any safety checks for bitpacks
9570 if (backing_int_val.isUndef(zcu)) return pt.undefRef(dest_ty);
9571 return .fromValue(try pt.bitpackValue(dest_ty, backing_int_val));
9572 }
9573 const enum_obj = ip.loadEnumType(dest_ty.toIntern());
9574 if (backing_int_val.isUndef(zcu)) {
9575 if (enum_obj.nonexhaustive) return pt.undefRef(dest_ty);
9576 return sema.failWithUseOfUndef(block, operand_src, null);
9577 }
9578 if (!enum_obj.nonexhaustive and
9579 enum_obj.tagValueIndex(ip, backing_int_val.toIntern()) == null)
9580 {
9581 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
9582 dest_ty.fmt(pt), backing_int_val.fmtValueSema(pt, sema),
9583 });
9584 }
9585 return .fromValue(try pt.enumValue(dest_ty, backing_int_val));
9586 }
9587
9588 switch (dest_ty.classify(zcu)) {
9589 .partially_comptime, .fully_comptime => unreachable, // does not apply to enums or bitpacks
9590 .no_possible_value => unreachable, // backing int also NPV, cannot coerce to NPV type
9591 .one_possible_value => unreachable, // backing int also OPV, should have been resolve above
9592 .runtime => {},
9593 }
9594
9595 if (block.wantSafety()) {
9596 try sema.preparePanicId(src, .invalid_enum_value);
9597 return block.addTyOp(.bit_cast_safe, dest_ty, backing_int_ref);
9598 }
9599 return block.addTyOp(.bit_cast, dest_ty, backing_int_ref);
94009600}
94019601
94029602fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9929,6 +10129,9 @@ fn analyzeSwitchBlock(
992910129 operand_ty.containerLayout(zcu) != .@"packed";
993010130 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
993110131
10132 // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909)
10133 if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated
10134
993210135 const cond_ref = switch (operand) {
993310136 .simple => |s| s.cond,
993410137 .loop => |l| l.init_cond,
......@@ -10433,26 +10636,11 @@ fn finishSwitchBr(
1043310636 }
1043410637
1043510638 var prev_result_overflowed = false;
10436 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
10437 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
10438 .int => .{ item, operand_ty },
10439 .@"enum" => b: {
10440 const int_val: Value = .fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
10441 break :b .{ int_val, int_val.typeOf(zcu) };
10442 },
10443 else => unreachable,
10444 };
10639 while (item.compareScalar(.lte, item_last, item_ty, zcu)) : ({
1044510640 assert(!prev_result_overflowed);
10446 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
10641 const result = try arith.incrementDefinedInt(sema, item_ty, item);
1044710642 prev_result_overflowed = result.overflow;
10448 item = switch (operand_ty.zigTypeTag(zcu)) {
10449 .int => result.val,
10450 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
10451 .ty = operand_ty.toIntern(),
10452 .int = result.val.toIntern(),
10453 } })),
10454 else => unreachable,
10455 };
10643 item = result.val;
1045610644 }) {
1045710645 cases_len += 1;
1045810646 case_block.instructions.clearRetainingCapacity();
......@@ -10585,7 +10773,7 @@ fn finishSwitchBr(
1058510773 break :check_enumerable .{ undefined, min_int };
1058610774 },
1058710775 .@"union", .@"struct" => {
10588 const backing_int_ty = item_ty.bitpackBackingInt(zcu);
10776 const backing_int_ty = item_ty.backingIntType(zcu);
1058910777 const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty);
1059010778 break :check_enumerable .{ undefined, min_backing_int };
1059110779 },
......@@ -10886,7 +11074,7 @@ const ValidatedSwitchBlock = struct {
1088611074 var cur_val = it.next_val orelse return null;
1088711075 const int_ty = switch (type_tag) {
1088811076 .int => item_ty,
10889 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),
11077 .@"union", .@"struct" => item_ty.backingIntType(zcu),
1089011078 else => unreachable,
1089111079 };
1089211080 while (it.next_idx < it.seen_ranges.len and
......@@ -11316,7 +11504,7 @@ fn validateSwitchBlock(
1131611504 check_range: {
1131711505 const int_ty = switch (type_tag) {
1131811506 .int => item_ty,
11319 .@"union", .@"struct" => item_ty.bitpackBackingInt(zcu),
11507 .@"union", .@"struct" => item_ty.backingIntType(zcu),
1132011508 else => unreachable,
1132111509 };
1132211510 const min_int = try int_ty.minInt(pt, int_ty);
......@@ -11938,14 +12126,7 @@ fn analyzeSwitchCaptures(
1193812126 try sema.analyzeUnreachable(case_block, operand_src, false);
1193912127 break :payload_ref .unreachable_value;
1194012128 };
11941 if (sema.resolveValue(loaded_operand)) |err_val| {
11942 break :payload_ref .fromIntern(try pt.intern(.{ .err = .{
11943 .ty = capture_err_ty.toIntern(),
11944 .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name,
11945 } }));
11946 } else {
11947 break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand);
11948 }
12129 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
1194912130 },
1195012131 .item_refs => |item_refs| {
1195112132 var names: InferredErrorSet.NameMap = .{};
......@@ -11955,14 +12136,7 @@ fn analyzeSwitchCaptures(
1195512136 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});
1195612137 }
1195712138 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());
11958 if (sema.resolveValue(loaded_operand)) |err_val| {
11959 break :payload_ref .fromIntern(try pt.intern(.{ .err = .{
11960 .ty = capture_err_ty.toIntern(),
11961 .name = zcu.intern_pool.indexToKey(err_val.toIntern()).err.name,
11962 } }));
11963 } else {
11964 break :payload_ref try case_block.addTyOp(.error_cast, capture_err_ty, loaded_operand);
11965 }
12139 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
1196612140 },
1196712141 }
1196812142 }
......@@ -12443,7 +12617,7 @@ fn validateSwitchItemOrRange(
1244312617 .first = .fromInterned(backing_int_val),
1244412618 .last = .fromInterned(backing_int_val),
1244512619 .src = item_src,
12446 }, item_ty.bitpackBackingInt(zcu), zcu);
12620 }, item_ty.backingIntType(zcu), zcu);
1244712621 },
1244812622 .enum_literal, .@"fn", .pointer, .type => {
1244912623 break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev|
......@@ -18400,7 +18574,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1840018574 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);
1840118575
1840218576 if (union_ty.containerLayout(zcu) == .@"packed") {
18403 return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node));
18577 return sema.bitCastUnchecked(block, union_ty, payload);
1840418578 }
1840518579
1840618580 if (sema.resolveValue(payload)) |payload_val| {
......@@ -18537,7 +18711,7 @@ fn zirStructInit(
1853718711 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
1853818712
1853918713 if (resolved_ty.containerLayout(zcu) == .@"packed") {
18540 const union_val = try sema.bitCast(block, resolved_ty, init_inst, src);
18714 const union_val = try sema.bitCastUnchecked(block, resolved_ty, init_inst);
1854118715 const result_val = try sema.coerce(block, result_ty, union_val, src);
1854218716 if (is_ref) {
1854318717 return sema.analyzeRef(block, src, result_val, .none);
......@@ -29716,41 +29890,29 @@ fn storePtrVal(
2971629890 }
2971729891}
2971829892
29719fn bitCast(
29893/// Asserts that the layout of `dest_ty` is already resolved.
29894fn bitCastUnchecked(
2972029895 sema: *Sema,
2972129896 block: *Block,
2972229897 dest_ty: Type,
2972329898 inst: Air.Inst.Ref,
29724 inst_src: LazySrcLoc,
2972529899) CompileError!Air.Inst.Ref {
29726 const pt = sema.pt;
29727 const zcu = pt.zcu;
29900 const zcu = sema.pt.zcu;
2972829901 const old_ty = sema.typeOf(inst);
2972929902
2973029903 old_ty.assertHasLayout(zcu);
29731 try sema.ensureLayoutResolved(dest_ty, inst_src, .init);
29904 dest_ty.assertHasLayout(zcu);
2973229905
2973329906 assert(old_ty.hasBitRepresentation(zcu));
2973429907 assert(dest_ty.hasBitRepresentation(zcu));
2973529908 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
2973629909 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
29737
29738 const dest_bits = dest_ty.bitSize(zcu);
29739 const old_bits = old_ty.bitSize(zcu);
29740
29741 if (old_bits != dest_bits) {
29742 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
29743 dest_ty.fmt(pt),
29744 dest_bits,
29745 old_ty.fmt(pt),
29746 old_bits,
29747 });
29748 }
29910 assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu));
2974929911
2975029912 if (sema.resolveValue(inst)) |val| {
2975129913 return .fromValue(try sema.bitCastVal(val, dest_ty));
2975229914 }
29753 try sema.validateRuntimeValue(block, inst_src, inst);
29915
2975429916 return block.addTyOp(.bit_cast, dest_ty, inst);
2975529917}
2975629918
......@@ -29774,6 +29936,26 @@ pub fn bitCastVal(
2977429936 }
2977529937}
2977629938
29939fn errorCastUnchecked(
29940 sema: *Sema,
29941 block: *Block,
29942 dest_ty: Type,
29943 inst: Air.Inst.Ref,
29944) CompileError!Air.Inst.Ref {
29945 const pt = sema.pt;
29946 const zcu = pt.zcu;
29947 assert(dest_ty.zigTypeTag(zcu) == .error_set);
29948 assert(sema.typeOf(inst).zigTypeTag(zcu) == .error_set);
29949 if (sema.resolveValue(inst)) |val| {
29950 if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
29951 return .fromIntern(try pt.intern(.{ .err = .{
29952 .ty = dest_ty.toIntern(),
29953 .name = zcu.intern_pool.indexToKey(val.toIntern()).err.name,
29954 } }));
29955 }
29956 return block.addTyOp(.error_cast, dest_ty, inst);
29957}
29958
2977729959fn checkSpirvSliceAllowed(
2977829960 sema: *Sema,
2977929961 block: *Block,
......@@ -33942,14 +34124,6 @@ fn intFromFloatScalar(
3394234124 return pt.getCoerced(cti_result, int_ty);
3394334125}
3394434126
33945fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
33946 const pt = sema.pt;
33947 if (!int_val.compareAllWithZero(.gte, pt.zcu)) return false;
33948 const end_val = try pt.intValue(tag_ty, end);
33949 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
33950 return true;
33951}
33952
3395334127/// Asserts the type is an exhaustive enum.
3395434128fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
3395534129 const pt = sema.pt;
src/Sema/reinterpret.zig+4-4
......@@ -353,7 +353,7 @@ const PackValueBytes = struct {
353353 return pt.aggregateValue(ty, elems);
354354 },
355355 .@"packed" => {
356 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
356 const backing_int_val = try pack.primitive(ty.backingIntType(zcu));
357357 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);
358358 return pt.bitpackValue(ty, backing_int_val);
359359 },
......@@ -424,15 +424,15 @@ const PackValueBytes = struct {
424424 }
425425 },
426426 .@"packed" => {
427 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
427 const backing_int_val = try pack.primitive(ty.backingIntType(zcu));
428428 if (backing_int_val.isUndef(zcu)) return pt.undefValue(ty);
429429 return pt.bitpackValue(ty, backing_int_val);
430430 },
431431 },
432432 .@"enum" => {
433 const tag_int_val = try pack.primitive(ty.intTagType(zcu));
433 const tag_int_val = try pack.primitive(ty.backingIntType(zcu));
434434 if (tag_int_val.isUndef(zcu)) return pt.undefValue(ty);
435 return pt.enumValue(ty, tag_int_val.toIntern());
435 return pt.enumValue(ty, tag_int_val);
436436 },
437437 else => return pack.primitive(ty),
438438 }
src/Sema/type_resolution.zig+1-1
......@@ -63,7 +63,7 @@ pub const LayoutResolveReason = enum {
6363 .@"export" => "for export here",
6464 .@"extern" => "for extern declaration here",
6565 .asm_out_type => "for inline assembly output type declared here",
66 .std_lang_type => "from 'std.lang'",
66 .std_lang_type => "from 'std.lang'",
6767 // zig fmt: on
6868 };
6969 }
src/Type.zig+27-27
......@@ -1580,15 +1580,28 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.lang.Type.ContainerLayout
15801580 };
15811581}
15821582
1583pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type {
1583/// Asserts that the type is either an enum or a bitpack.
1584pub fn backingIntType(ty: Type, zcu: *const Zcu) Type {
15841585 const ip = &zcu.intern_pool;
15851586 return switch (ip.indexToKey(ty.toIntern())) {
1587 .enum_type => .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
15861588 .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
15871589 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
15881590 else => unreachable,
15891591 };
15901592}
15911593
1594/// For unions, returns the *backing int* mode, not the *enum tag* mode.
1595pub fn backingIntMode(ty: Type, zcu: *const Zcu) InternPool.BackingTypeMode {
1596 const ip = &zcu.intern_pool;
1597 return switch (ip.indexToKey(ty.toIntern())) {
1598 .enum_type => ip.loadEnumType(ty.toIntern()).int_tag_mode,
1599 .struct_type => ip.loadStructType(ty.toIntern()).packed_backing_mode,
1600 .union_type => ip.loadUnionType(ty.toIntern()).packed_backing_mode,
1601 else => unreachable,
1602 };
1603}
1604
15921605/// Asserts that the type is an error union.
15931606pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
15941607 return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);
......@@ -2093,11 +2106,8 @@ pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {
20932106 return try pt.unionValue(ty, tag_val, payload_val);
20942107 } else unreachable;
20952108 },
2096 .enum_type => if (try ty.intTagType(zcu).onePossibleValue(pt)) |int_tag_opv| {
2097 return .fromInterned(try pt.intern(.{ .enum_tag = .{
2098 .ty = ty.toIntern(),
2099 .int = int_tag_opv.toIntern(),
2100 } }));
2109 .enum_type => if (try ty.backingIntType(zcu).onePossibleValue(pt)) |int_tag_opv| {
2110 return try pt.enumValue(ty, int_tag_opv);
21012111 } else null,
21022112
21032113 // values, not types
......@@ -2274,17 +2284,6 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
22742284 return pt.intValue_big(dest_ty, res.toConst());
22752285}
22762286
2277/// Asserts the type is an enum or a union.
2278pub fn intTagType(ty: Type, zcu: *const Zcu) Type {
2279 const ip = &zcu.intern_pool;
2280 const enum_ty: Type = switch (ip.indexToKey(ty.toIntern())) {
2281 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_type),
2282 .enum_type => ty,
2283 else => unreachable,
2284 };
2285 return .fromInterned(ip.loadEnumType(enum_ty.toIntern()).int_tag_type);
2286}
2287
22882287pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {
22892288 const ip = &zcu.intern_pool;
22902289 return switch (ip.indexToKey(ty.toIntern())) {
......@@ -3058,15 +3057,12 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
30583057 .one, .many, .c => .pointer,
30593058 },
30603059
3061 .@"enum" => {
3062 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
3063 return switch (enum_obj.int_tag_mode) {
3064 .explicit => if (enum_obj.int_tag_type != .noreturn_type)
3065 null
3066 else
3067 .other,
3068 .auto => .{ .enum_inferred_int_tag = ty },
3069 };
3060 .@"enum" => switch (ty.backingIntMode(zcu)) {
3061 .explicit => switch (ty.backingIntType(zcu).toIntern()) {
3062 else => null,
3063 .noreturn_type => .other,
3064 },
3065 .auto => .{ .enum_inferred_int_tag = ty },
30703066 },
30713067
30723068 .@"struct" => switch (ty.containerLayout(zcu)) {
......@@ -3231,7 +3227,11 @@ pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {
32313227 .float,
32323228 => true,
32333229
3234 .@"enum" => zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode == .explicit,
3230 .@"enum" => {
3231 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
3232 return enum_obj.int_tag_mode == .explicit and
3233 enum_obj.int_tag_type != .noreturn_type;
3234 },
32353235 .pointer, .optional => ty.isPtrAtRuntime(zcu),
32363236 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
32373237
src/Value.zig+11-6
......@@ -146,8 +146,13 @@ pub fn toType(self: Value) Type {
146146 return Type.fromInterned(self.toIntern());
147147}
148148
149pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {
150 return .fromInterned(zcu.intern_pool.indexToKey(val.toIntern()).enum_tag.int);
149/// Asserts that value is defined and of enum or bitpack type.
150pub fn backingInt(val: Value, zcu: *const Zcu) Value {
151 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
152 .enum_tag => |enum_tag| .fromInterned(enum_tag.int),
153 .bitpack => |bitpack| .fromInterned(bitpack.backing_int_val),
154 else => unreachable,
155 };
151156}
152157
153158/// Asserts that `val` is an integer.
......@@ -415,7 +420,7 @@ pub fn writeToPackedMemory(
415420 }
416421 },
417422 .@"enum" => {
418 const int_val = val.intFromEnum(zcu);
423 const int_val = val.backingInt(zcu);
419424 int_val.writeToPackedMemory(zcu, buffer, bit_offset);
420425 },
421426 .int => {
......@@ -564,7 +569,7 @@ pub fn readFromPackedMemory(
564569 return pt.intValue_big(ty, bigint.toConst());
565570 },
566571 .@"enum" => {
567 const int_ty = ty.intTagType(zcu);
572 const int_ty = ty.backingIntType(zcu);
568573 const int_val: Value = try .readFromPackedMemory(int_ty, pt, buffer, bit_offset);
569574 return pt.getCoerced(int_val, ty);
570575 },
......@@ -581,7 +586,7 @@ pub fn readFromPackedMemory(
581586 } })),
582587 .@"struct", .@"union" => {
583588 assert(ty.containerLayout(zcu) == .@"packed");
584 const int_val: Value = try .readFromPackedMemory(ty.bitpackBackingInt(zcu), pt, buffer, bit_offset);
589 const int_val: Value = try .readFromPackedMemory(ty.backingIntType(zcu), pt, buffer, bit_offset);
585590 return pt.bitpackValue(ty, int_val);
586591 },
587592 .array, .vector => {
......@@ -2368,7 +2373,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory
23682373 try pt.nullValue(ty),
23692374
23702375 .@"enum" => switch (interpret_mode) {
2371 .direct => try pt.enumValue(ty, (try uninterpret(@intFromEnum(val), ty.intTagType(zcu), pt)).toIntern()),
2376 .direct => try pt.enumValue(ty, try uninterpret(@intFromEnum(val), ty.backingIntType(zcu), pt)),
23722377 .by_name => {
23732378 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls);
23742379 const field_idx = ty.enumFieldIndex(field_name_ip, zcu) orelse return error.TypeMismatch;
src/Zcu/PerThread.zig+16-19
......@@ -3956,7 +3956,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
39563956}
39573957
39583958pub fn intType(pt: Zcu.PerThread, signedness: std.lang.Signedness, bits: u16) Allocator.Error!Type {
3959 return Type.fromInterned(try pt.intern(.{ .int_type = .{
3959 return .fromInterned(try pt.intern(.{ .int_type = .{
39603960 .signedness = signedness,
39613961 .bits = bits,
39623962 } }));
......@@ -3967,15 +3967,15 @@ pub fn errorIntType(pt: Zcu.PerThread) std.mem.Allocator.Error!Type {
39673967}
39683968
39693969pub fn arrayType(pt: Zcu.PerThread, info: InternPool.Key.ArrayType) Allocator.Error!Type {
3970 return Type.fromInterned(try pt.intern(.{ .array_type = info }));
3970 return .fromInterned(try pt.intern(.{ .array_type = info }));
39713971}
39723972
39733973pub fn vectorType(pt: Zcu.PerThread, info: InternPool.Key.VectorType) Allocator.Error!Type {
3974 return Type.fromInterned(try pt.intern(.{ .vector_type = info }));
3974 return .fromInterned(try pt.intern(.{ .vector_type = info }));
39753975}
39763976
39773977pub fn optionalType(pt: Zcu.PerThread, child_type: InternPool.Index) Allocator.Error!Type {
3978 return Type.fromInterned(try pt.intern(.{ .opt_type = child_type }));
3978 return .fromInterned(try pt.intern(.{ .opt_type = child_type }));
39793979}
39803980
39813981pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!Type {
......@@ -3997,7 +3997,7 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!
39973997 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
39983998 }
39993999
4000 return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
4000 return .fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
40014001}
40024002
40034003pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {
......@@ -4037,11 +4037,11 @@ pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Err
40374037/// Use this for `anyframe->T` only.
40384038/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
40394039pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {
4040 return Type.fromInterned(try pt.intern(.{ .anyframe_type = payload_ty.toIntern() }));
4040 return .fromInterned(try pt.intern(.{ .anyframe_type = payload_ty.toIntern() }));
40414041}
40424042
40434043pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {
4044 return Type.fromInterned(try pt.intern(.{ .error_union_type = .{
4044 return .fromInterned(try pt.intern(.{ .error_union_type = .{
40454045 .error_set_type = error_set_ty.toIntern(),
40464046 .payload_type = payload_ty.toIntern(),
40474047 } }));
......@@ -4050,7 +4050,7 @@ pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) A
40504050pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type {
40514051 const names: *const [1]InternPool.NullTerminatedString = &name;
40524052 const comp = pt.zcu.comp;
4053 return Type.fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names));
4053 return .fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names));
40544054}
40554055
40564056/// Sorts `names` in place.
......@@ -4066,7 +4066,7 @@ pub fn errorSetFromUnsortedNames(
40664066 );
40674067 const comp = pt.zcu.comp;
40684068 const new_ty = try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names);
4069 return Type.fromInterned(new_ty);
4069 return .fromInterned(new_ty);
40704070}
40714071
40724072/// Supports only pointers, not pointer-like optionals.
......@@ -4074,7 +4074,7 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
40744074 const zcu = pt.zcu;
40754075 assert(ty.zigTypeTag(zcu) == .pointer and !ty.isSlice(zcu));
40764076 assert(x != 0 or ty.isAllowzeroPtr(zcu));
4077 return Value.fromInterned(try pt.intern(.{ .ptr = .{
4077 return .fromInterned(try pt.intern(.{ .ptr = .{
40784078 .ty = ty.toIntern(),
40794079 .base_addr = .int,
40804080 .byte_offset = x,
......@@ -4082,14 +4082,11 @@ pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
40824082}
40834083
40844084/// Creates an enum tag value based on the integer tag value.
4085pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value {
4086 if (std.debug.runtime_safety) {
4087 const tag = ty.zigTypeTag(pt.zcu);
4088 assert(tag == .@"enum");
4089 }
4090 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
4085pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: Value) Allocator.Error!Value {
4086 if (std.debug.runtime_safety) assert(ty.zigTypeTag(pt.zcu) == .@"enum");
4087 return .fromInterned(try pt.intern(.{ .enum_tag = .{
40914088 .ty = ty.toIntern(),
4092 .int = tag_int,
4089 .int = tag_int.toIntern(),
40934090 } }));
40944091}
40954092
......@@ -4103,7 +4100,7 @@ pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Alloca
41034100
41044101 if (enum_type.field_values.len == 0) {
41054102 // Auto-numbered fields.
4106 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
4103 return .fromInterned(try pt.intern(.{ .enum_tag = .{
41074104 .ty = ty.toIntern(),
41084105 .int = try pt.intern(.{ .int = .{
41094106 .ty = enum_type.int_tag_type,
......@@ -4262,7 +4259,7 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value
42624259
42634260/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value.
42644261pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value {
4265 assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.bitpackBackingInt(pt.zcu).toIntern());
4262 assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.backingIntType(pt.zcu).toIntern());
42664263 return .fromInterned(try pt.intern(.{ .bitpack = .{
42674264 .ty = ty.toIntern(),
42684265 .backing_int_val = backing_int_val.toIntern(),
src/codegen.zig+1-1
......@@ -415,7 +415,7 @@ pub fn generateSymbol(
415415 }
416416 },
417417 .enum_tag => |enum_tag| {
418 const int_tag_ty = ty.intTagType(zcu);
418 const int_tag_ty = ty.backingIntType(zcu);
419419 try generateSymbol(bin_file, pt, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent);
420420 },
421421 .float => |float| storage: switch (float.storage) {
src/codegen/aarch64.zig+4-2
......@@ -5,8 +5,10 @@ pub const encoding = @import("aarch64/encoding.zig");
55pub const Mir = @import("aarch64/Mir.zig");
66pub const Select = @import("aarch64/Select.zig");
77
8pub fn legalizeFeatures(_: *const std.Target) ?*Air.Legalize.Features {
9 return null;
8pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
9 return comptime &.initMany(&.{
10 .expand_bit_cast_safe,
11 });
1012}
1113
1214pub fn generate(
src/codegen/aarch64/Select.zig+2
......@@ -362,6 +362,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
362362 air_inst_index = air_body[air_body_index];
363363 continue :air_tag air_tags[@intFromEnum(air_inst_index)];
364364 },
365 .bit_cast_safe => unreachable, // legalized
365366 inline .block, .dbg_inline_block => |air_tag| {
366367 const air_body_block = switch (air_tag) {
367368 else => comptime unreachable,
......@@ -3201,6 +3202,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
32013202 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
32023203 },
32033204 .bit_cast,
3205 .bit_cast_safe, // TODO safety check
32043206 .ptr_cast,
32053207 .ptr_from_int,
32063208 .int_from_ptr,
src/codegen/c.zig+4-2
......@@ -27,6 +27,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
2727 return comptime switch (dev.env.supports(.legalize)) {
2828 inline false, true => |supports_legalize| &.init(.{
2929 // we don't currently ask zig1 to use safe optimization modes
30 .expand_bit_cast_safe = supports_legalize,
3031 .expand_int_cast_safe = supports_legalize,
3132 .expand_int_from_float_safe = supports_legalize,
3233 .expand_int_from_float_optimized_safe = supports_legalize,
......@@ -1460,7 +1461,7 @@ pub const DeclGen = struct {
14601461 }
14611462 return w.writeByte('}');
14621463 },
1463 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1464 .@"packed" => return dg.renderUndefValue(w, ty.backingIntType(zcu), location),
14641465 }
14651466 },
14661467 .tuple_type => |tuple_info| {
......@@ -1520,7 +1521,7 @@ pub const DeclGen = struct {
15201521 if (loaded_union.has_runtime_tag) try w.writeByte(' ');
15211522 if (loaded_union.layout == .auto) try w.writeByte('}');
15221523 },
1523 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1524 .@"packed" => return dg.renderUndefValue(w, ty.backingIntType(zcu), location),
15241525 }
15251526 },
15261527 .error_union_type => |error_union| {
......@@ -2876,6 +2877,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
28762877 .add_safe,
28772878 .sub_safe,
28782879 .mul_safe,
2880 .bit_cast_safe,
28792881 .int_cast_safe,
28802882 .int_from_float_safe,
28812883 .int_from_float_optimized_safe,
src/codegen/c/type.zig+1-2
......@@ -484,8 +484,7 @@ pub const CType = union(enum) {
484484 pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass {
485485 const int_ty: Type = switch (ty.zigTypeTag(zcu)) {
486486 .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu),
487 .@"enum" => ty.intTagType(zcu),
488 .@"struct", .@"union" => ty.bitpackBackingInt(zcu),
487 .@"enum", .@"struct", .@"union" => ty.backingIntType(zcu),
489488 .int => ty,
490489 else => unreachable,
491490 };
src/codegen/c/type/render_defs.zig+2-2
......@@ -212,7 +212,7 @@ pub fn defineComplete(
212212 },
213213 .@"enum" => {
214214 const name_cty: CType = .{ .@"enum" = ty };
215 const cty: CType = try .lower(ty.intTagType(zcu), deps, arena, zcu);
215 const cty: CType = try .lower(ty.backingIntType(zcu), deps, arena, zcu);
216216 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
217217 cty.fmtDeclaratorPrefix(zcu),
218218 name_cty.fmtTypeName(zcu),
......@@ -343,7 +343,7 @@ fn defineBitpack(
343343) (Allocator.Error || Writer.Error)!void {
344344 const zcu = pt.zcu;
345345 const name_cty: CType = .{ .bitpack = ty };
346 const cty: CType = try .lower(ty.bitpackBackingInt(zcu), deps, arena, zcu);
346 const cty: CType = try .lower(ty.backingIntType(zcu), deps, arena, zcu);
347347 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
348348 cty.fmtDeclaratorPrefix(zcu),
349349 name_cty.fmtTypeName(zcu),
src/codegen/llvm.zig+1-1
......@@ -3252,7 +3252,7 @@ pub const Object = struct {
32523252 return ty;
32533253 },
32543254 .opaque_type, .spirv_type => unreachable, // no runtime bits
3255 .enum_type => try o.lowerType(t.intTagType(zcu), repr),
3255 .enum_type => try o.lowerType(t.backingIntType(zcu), repr),
32563256 .func_type => |func_type| try o.lowerFnType(t, func_type),
32573257 .error_set_type, .inferred_error_set_type => try o.errorIntType(repr),
32583258 // values, not types
src/codegen/llvm/FuncGen.zig+24-5
......@@ -465,7 +465,8 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
465465 .alloc => try self.airAlloc(inst),
466466 .ret_ptr => try self.airRetPtr(inst),
467467 .arg => try self.airArg(inst),
468 .bit_cast => try self.airBitCast(inst),
468 .bit_cast => try self.airBitCast(inst, false),
469 .bit_cast_safe => try self.airBitCast(inst, true),
469470 .ptr_cast => try self.airNopCast(inst),
470471 .ptr_from_int => try self.airPtrFromInt(inst),
471472 .int_from_ptr => try self.airIntFromPtr(inst),
......@@ -1254,7 +1255,6 @@ fn cmp(
12541255 const zcu = o.zcu;
12551256 const scalar_ty = operand_ty.scalarType(zcu);
12561257 const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
1257 .@"enum" => scalar_ty.intTagType(zcu),
12581258 .int, .bool, .pointer, .error_set => scalar_ty,
12591259 .optional => blk: {
12601260 const payload_ty = operand_ty.optionalChild(zcu);
......@@ -1328,7 +1328,7 @@ fn cmp(
13281328 return phi.toValue();
13291329 },
13301330 .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),
1331 .@"struct", .@"union" => scalar_ty.bitpackBackingInt(zcu),
1331 .@"enum", .@"struct", .@"union" => scalar_ty.backingIntType(zcu),
13321332 else => unreachable,
13331333 };
13341334 const is_signed = int_ty.isSignedInt(zcu);
......@@ -4539,7 +4539,7 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
45394539 }
45404540}
45414541
4542fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
4542fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value {
45434543 const o = fg.object;
45444544 const zcu = o.zcu;
45454545
......@@ -4564,7 +4564,26 @@ fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value
45644564 assert(!isByRef(dest_ty, zcu));
45654565
45664566 const llvm_dest_ty = try o.lowerType(dest_ty, .by_value);
4567 return fg.wip.cast(.bitcast, operand, llvm_dest_ty, "");
4567 const result = try fg.wip.cast(.bitcast, operand, llvm_dest_ty, "");
4568 if (safety and dest_ty.zigTypeTag(zcu) == .@"enum" and !dest_ty.isNonexhaustiveEnum(zcu)) {
4569 const llvm_fn = try o.getIsNamedEnumValueFunction(dest_ty);
4570 const is_valid_enum_val = try fg.wip.call(
4571 .normal,
4572 .fastcc,
4573 .none,
4574 llvm_fn.typeOf(&o.builder),
4575 llvm_fn.toValue(&o.builder),
4576 &.{result},
4577 "",
4578 );
4579 const fail_block = try fg.wip.block(1, "ValidEnumFail");
4580 const ok_block = try fg.wip.block(1, "ValidEnumOk");
4581 _ = try fg.wip.brCond(is_valid_enum_val, ok_block, fail_block, .none);
4582 fg.wip.cursor = .{ .block = fail_block };
4583 try fg.buildSimplePanic(.invalid_enum_value);
4584 fg.wip.cursor = .{ .block = ok_block };
4585 }
4586 return result;
45684587}
45694588
45704589fn airNopCast(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
src/codegen/riscv64/CodeGen.zig+3-2
......@@ -51,6 +51,7 @@ const InnerError = codegen.Error || error{OutOfRegisters};
5151
5252pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
5353 return comptime &.initMany(&.{
54 .expand_bit_cast_safe,
5455 .expand_int_cast_safe,
5556 .expand_int_from_float_safe,
5657 .expand_int_from_float_optimized_safe,
......@@ -1454,6 +1455,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
14541455 .add_safe,
14551456 .sub_safe,
14561457 .mul_safe,
1458 .bit_cast_safe,
14571459 .int_cast_safe,
14581460 .int_from_float_safe,
14591461 .int_from_float_optimized_safe,
......@@ -5128,7 +5130,6 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
51285130 .@"struct",
51295131 => {
51305132 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
5131 .@"enum" => lhs_ty.intTagType(zcu),
51325133 .int => lhs_ty,
51335134 .bool => .u1,
51345135 .pointer => .u64,
......@@ -5143,7 +5144,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
51435144 return func.fail("TODO riscv cmp non-pointer optionals", .{});
51445145 }
51455146 },
5146 .@"struct", .@"union" => lhs_ty.bitpackBackingInt(zcu),
5147 .@"enum", .@"struct", .@"union" => lhs_ty.backingIntType(zcu),
51475148 else => unreachable,
51485149 };
51495150
src/codegen/sparc64/CodeGen.zig+2-1
......@@ -697,6 +697,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
697697 .add_safe,
698698 .sub_safe,
699699 .mul_safe,
700 .bit_cast_safe,
700701 .int_cast_safe,
701702 .int_from_float_safe,
702703 .int_from_float_optimized_safe,
......@@ -1374,7 +1375,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
13741375
13751376 const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
13761377 .vector => unreachable, // Handled by cmp_vector.
1377 .@"enum" => lhs_ty.intTagType(zcu),
1378 .@"enum" => lhs_ty.backingIntType(zcu),
13781379 .int => lhs_ty,
13791380 .bool => .u1,
13801381 .pointer => .usize,
src/codegen/spirv/CodeGen.zig+11-10
......@@ -121,6 +121,7 @@ const StructType = struct {
121121
122122pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
123123 return comptime &.initMany(&.{
124 .expand_bit_cast_safe,
124125 .expand_int_cast_safe,
125126 .expand_int_from_float_safe,
126127 .expand_int_from_float_optimized_safe,
......@@ -1353,7 +1354,7 @@ fn arithmeticTypeInfo(cg: *CodeGen, ty: Type) ArithmeticTypeInfo {
13531354 const target = cg.zcu.getTarget();
13541355 var scalar_ty = ty.scalarType(zcu);
13551356 if (scalar_ty.zigTypeTag(zcu) == .@"enum") {
1356 scalar_ty = scalar_ty.intTagType(zcu);
1357 scalar_ty = scalar_ty.backingIntType(zcu);
13571358 }
13581359 const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;
13591360 return switch (scalar_ty.zigTypeTag(zcu)) {
......@@ -1732,8 +1733,8 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
17321733 return try cg.constructComposite(comp_ty_id, &constituents);
17331734 },
17341735 .enum_tag => {
1735 const int_val = val.intFromEnum(zcu);
1736 const int_ty = ty.intTagType(zcu);
1736 const int_val = val.backingInt(zcu);
1737 const int_ty = ty.backingIntType(zcu);
17371738 break :cache try cg.constant(int_ty, int_val, repr);
17381739 },
17391740 .ptr => return cg.constantPtr(val),
......@@ -2213,7 +2214,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
22132214 const int_info = ty.intInfo(zcu);
22142215 return try cg.intType(int_info.signedness, int_info.bits);
22152216 },
2216 .@"enum" => return try cg.resolveType(ty.intTagType(zcu), repr),
2217 .@"enum" => return try cg.resolveType(ty.backingIntType(zcu), repr),
22172218 .float => {
22182219 const bits = ty.floatBits(target);
22192220 const supported = switch (bits) {
......@@ -5807,7 +5808,7 @@ fn cmp(
58075808 .int, .bool, .float => {},
58085809 .@"enum" => {
58095810 assert(!is_vector);
5810 const ty = lhs.ty.intTagType(zcu);
5811 const ty = lhs.ty.backingIntType(zcu);
58115812 return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty));
58125813 },
58135814 .@"struct" => {
......@@ -6887,7 +6888,7 @@ fn unionInit(
68876888
68886889 const tag_int = if (layout.tag_size != 0) blk: {
68896890 const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field);
6890 const tag_int_val = tag_val.intFromEnum(zcu);
6891 const tag_int_val = tag_val.backingInt(zcu);
68916892 break :blk tag_int_val.toUnsignedInt(zcu);
68926893 } else 0;
68936894
......@@ -8164,7 +8165,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
81648165 break :blk if (backing_bits <= 32) 1 else 2;
81658166 },
81668167 .@"enum" => blk: {
8167 const int_ty = cond_ty.intTagType(zcu);
8168 const int_ty = cond_ty.backingIntType(zcu);
81688169 const int_info = int_ty.intInfo(zcu);
81698170 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);
81708171 if (big_int) return cg.todo("implement composite int switch", .{});
......@@ -8224,7 +8225,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
82248225 const value: Value = .fromInterned(item.toInterned().?);
82258226 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
82268227 .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),
8227 .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu),
8228 .@"enum" => value.backingInt(zcu).toUnsignedInt(zcu),
82288229 .error_set => value.getErrorInt(zcu),
82298230 .pointer => value.toUnsignedInt(zcu),
82308231 else => unreachable,
......@@ -8378,7 +8379,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
83788379 break :blk if (backing_bits <= 32) 1 else 2;
83798380 },
83808381 .@"enum" => blk: {
8381 const int_ty = cond_ty.intTagType(zcu);
8382 const int_ty = cond_ty.backingIntType(zcu);
83828383 const int_info = int_ty.intInfo(zcu);
83838384 const backing_bits, const big_int = cg.backingIntBits(int_info.bits);
83848385 if (big_int) return cg.todo("implement composite int loop switch", .{});
......@@ -8464,7 +8465,7 @@ fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
84648465 const value: Value = .fromInterned(item.toInterned().?);
84658466 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
84668467 .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),
8467 .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu),
8468 .@"enum" => value.backingInt(zcu).toUnsignedInt(zcu),
84688469 .error_set => value.getErrorInt(zcu),
84698470 .pointer => value.toUnsignedInt(zcu),
84708471 else => unreachable,
src/codegen/wasm/CodeGen.zig+5-3
......@@ -32,6 +32,7 @@ const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
3232
3333pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
3434 return comptime &.initMany(&.{
35 .expand_bit_cast_safe,
3536 .expand_int_cast_safe,
3637 .expand_int_from_float_safe,
3738 .expand_int_from_float_optimized_safe,
......@@ -615,7 +616,7 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w
615616 .unrolled => .i32,
616617 },
617618 .@"union", .@"struct" => switch (ty.containerLayout(zcu)) {
618 .@"packed" => typeToValtype(ty.bitpackBackingInt(zcu), zcu, target),
619 .@"packed" => typeToValtype(ty.backingIntType(zcu), zcu, target),
619620 .auto, .@"extern" => .i32,
620621 },
621622 else => .i32, // all represented as reference/immediate
......@@ -1226,7 +1227,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool {
12261227 .frame,
12271228 => return ty.hasRuntimeBits(zcu),
12281229 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
1229 .@"packed" => return isByRef(ty.bitpackBackingInt(zcu), zcu, target),
1230 .@"packed" => return isByRef(ty.backingIntType(zcu), zcu, target),
12301231 .@"extern", .auto => return ty.hasRuntimeBits(zcu),
12311232 },
12321233 .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled,
......@@ -1905,6 +1906,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19051906 .add_safe,
19061907 .sub_safe,
19071908 .mul_safe,
1909 .bit_cast_safe,
19081910 .int_cast_safe,
19091911 .int_from_float_safe,
19101912 .int_from_float_optimized_safe,
......@@ -5134,7 +5136,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
51345136 return .{ .imm32 = 0xaaaaaaaa };
51355137 },
51365138 .@"struct", .@"union" => {
5137 const backing_int_ty = ty.bitpackBackingInt(zcu);
5139 const backing_int_ty = ty.backingIntType(zcu);
51385140 return cg.emitUndefined(backing_int_ty);
51395141 },
51405142 else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}),
src/codegen/x86_64/CodeGen.zig+2
......@@ -63,6 +63,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
6363 .reduce_one_elem_to_bit_cast,
6464 .splat_one_elem_to_bit_cast,
6565
66 .expand_bit_cast_safe,
6667 .expand_int_cast_safe,
6768 .expand_int_from_float_safe,
6869 .expand_int_from_float_optimized_safe,
......@@ -67446,6 +67447,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
6744667447 .int_from_error,
6744767448 .union_from_enum,
6744867449 => try cg.airBitCast(inst),
67450 .bit_cast_safe => unreachable,
6744967451 .block => {
6745067452 const block = cg.air.unwrapBlock(inst);
6745167453 if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none);
src/link/Wasm/Flush.zig+1-1
......@@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
151151 assert(ip.indexToKey(data.ip_index) == .enum_type);
152152 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
153153 if (!gop.found_existing) {
154 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
154 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).backingIntType(zcu);
155155 gop.value_ptr.* = .{ .tag_name = .{
156156 .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}),
157157 .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target),
src/print_value.zig+18-12
......@@ -94,15 +94,14 @@ pub fn print(
9494 enum_literal.fmt(ip),
9595 }),
9696 .enum_tag => |enum_tag| {
97 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());
98 if (enum_type.tagValueIndex(ip, enum_tag.int)) |tag_index| {
99 return writer.print(".{f}", .{enum_type.field_names.get(ip)[tag_index].fmt(ip)});
97 const ty: Type = .fromInterned(enum_tag.ty);
98 const enum_obj = ip.loadEnumType(ty.toIntern());
99 if (enum_obj.tagValueIndex(ip, enum_tag.int)) |tag_index| {
100 return writer.print(".{f}", .{enum_obj.field_names.get(ip)[tag_index].fmt(ip)});
100101 }
101 if (level == 0) {
102 return writer.writeAll("@enumFromInt(...)");
103 }
104 try writer.writeAll("@enumFromInt(");
105 try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
102 try writer.writeAll("@fromBackingInt(");
103 if (level == 0) return writer.writeAll("...)");
104 try print(.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
106105 try writer.writeAll(")");
107106 },
108107 .float => |float| switch (float.storage) {
......@@ -190,10 +189,17 @@ pub fn print(
190189 try writer.writeAll(" }");
191190 return;
192191 },
193 .@"union" => {
194 try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)});
195 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
196 try writer.writeAll("))");
192 .@"union" => switch (ty.backingIntMode(zcu)) {
193 .auto => {
194 try writer.print("@bitCast(@as({f}, ", .{ty.backingIntType(zcu).fmt(pt)});
195 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
196 try writer.writeAll("))");
197 },
198 .explicit => {
199 try writer.writeAll("@fromBackingInt(");
200 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
201 try writer.writeAll(")");
202 },
197203 },
198204 else => unreachable,
199205 }
src/print_zir.zig+14
......@@ -193,6 +193,7 @@ const Writer = struct {
193193 .elem_type,
194194 .indexable_ptr_elem_type,
195195 .splat_op_result_ty,
196 .from_backing_int_arg_ty,
196197 .indexable_ptr_len,
197198 .anyframe_type,
198199 .bit_not,
......@@ -232,6 +233,7 @@ const Writer = struct {
232233 .compile_error,
233234 .set_eval_branch_quota,
234235 .int_from_enum,
236 .backing_int,
235237 .align_of,
236238 .int_from_bool,
237239 .embed_file,
......@@ -418,6 +420,8 @@ const Writer = struct {
418420
419421 .for_len => try self.writePlNodeMultiOp(stream, inst),
420422
423 .from_backing_int => try self.writePlNodeBin(stream, inst),
424
421425 .elem_val_imm => try self.writeElemValImm(stream, inst),
422426
423427 .@"export" => try self.writePlNodeExport(stream, inst),
......@@ -985,6 +989,16 @@ const Writer = struct {
985989 try self.writeSrcNode(stream, inst_data.src_node);
986990 }
987991
992 fn writeFromBackingInt(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
993 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
994 const extra = self.code.extraData(Zir.Inst.FromBackingInt, inst_data.payload_index);
995 try self.writeInstRef(stream, extra.data.result_type);
996 try stream.writeAll(", ");
997 try self.writeBracedBody(stream, self.code.bodySlice(extra.end, extra.data.body_len));
998 try stream.writeAll(") ");
999 try self.writeSrcNode(stream, inst_data.src_node);
1000 }
1001
9881002 fn writeBuiltinCall(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
9891003 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
9901004 const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
test/behavior.zig+1
......@@ -6,6 +6,7 @@ test {
66 _ = @import("behavior/alignof.zig");
77 _ = @import("behavior/array.zig");
88 _ = @import("behavior/atomics.zig");
9 _ = @import("behavior/backing_int.zig");
910 _ = @import("behavior/basic.zig");
1011 _ = @import("behavior/bit_shifting.zig");
1112 _ = @import("behavior/bitcast.zig");
test/behavior/backing_int.zig created+378
......@@ -0,0 +1,378 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const expect = std.testing.expect;
4const assert = std.debug.assert;
5
6const E1 = enum(u8) {
7 a,
8 b,
9 c,
10 d,
11 const expected = .{
12 .val = @as(E1, .b),
13 .int = @as(@typeInfo(E1).@"enum".tag_type, 1),
14 };
15};
16const E2 = enum(i20) {
17 x,
18 y,
19 z = -5,
20 const expected = .{
21 .val = @as(E2, .z),
22 .int = @as(@typeInfo(E2).@"enum".tag_type, -5),
23 };
24};
25const E3 = enum(i32) {
26 _,
27 const zero: E3 = @bitCast(@as(i32, 0));
28 const expected = .{
29 .val = @as(E3, .zero),
30 .int = @as(@typeInfo(E3).@"enum".tag_type, 0),
31 };
32};
33const E4 = enum(i200) {
34 min = -(1 << 199),
35 const expected = .{
36 .val = @as(E4, .min),
37 .int = @as(@typeInfo(E4).@"enum".tag_type, -(1 << 199)),
38 };
39};
40const E5 = enum(u0) {
41 a,
42 const expected = .{
43 .val = @as(E5, .a),
44 .int = @as(@typeInfo(E5).@"enum".tag_type, 0),
45 };
46};
47
48test "@backingInt with enums" {
49 const static = struct {
50 fn doTheTest(v1: E1, v2: E2, v3: E3, v4: E4, v5: E5) !void {
51 const b1 = @backingInt(v1);
52 comptime assert(@TypeOf(b1) == @typeInfo(E1).@"enum".tag_type);
53 try expect(b1 == E1.expected.int);
54
55 const b2 = @backingInt(v2);
56 comptime assert(@TypeOf(b2) == @typeInfo(E2).@"enum".tag_type);
57 try expect(b2 == E2.expected.int);
58
59 const b3 = @backingInt(v3);
60 comptime assert(@TypeOf(b3) == @typeInfo(E3).@"enum".tag_type);
61 try expect(b3 == E3.expected.int);
62
63 const b4 = @backingInt(v4);
64 comptime assert(@TypeOf(b4) == @typeInfo(E4).@"enum".tag_type);
65 try expect(b4 == E4.expected.int);
66
67 const b5 = @backingInt(v5);
68 comptime assert(@TypeOf(b5) == @typeInfo(E5).@"enum".tag_type);
69 try expect(b5 == E5.expected.int);
70 }
71 };
72 try static.doTheTest(E1.expected.val, E2.expected.val, E3.expected.val, E4.expected.val, E5.expected.val);
73 try comptime static.doTheTest(E1.expected.val, E2.expected.val, E3.expected.val, E4.expected.val, E5.expected.val);
74}
75
76test "@fromBackingInt with enums" {
77 const static = struct {
78 fn doTheTest(
79 b1: @typeInfo(E1).@"enum".tag_type,
80 b2: @typeInfo(E2).@"enum".tag_type,
81 b3: @typeInfo(E3).@"enum".tag_type,
82 b4: @typeInfo(E4).@"enum".tag_type,
83 b5: @typeInfo(E5).@"enum".tag_type,
84 ) !void {
85 const v1: E1 = @fromBackingInt(b1);
86 try expect(v1 == E1.expected.val);
87
88 const v2: E2 = @fromBackingInt(b2);
89 try expect(v2 == E2.expected.val);
90
91 const v3: E3 = @fromBackingInt(b3);
92 try expect(v3 == E3.expected.val);
93
94 const v4: E4 = @fromBackingInt(b4);
95 try expect(v4 == E4.expected.val);
96
97 const v5: E5 = @fromBackingInt(b5);
98 try expect(v5 == E5.expected.val);
99 }
100 };
101 try static.doTheTest(E1.expected.int, E2.expected.int, E3.expected.int, E4.expected.int, E5.expected.int);
102 try comptime static.doTheTest(E1.expected.int, E2.expected.int, E3.expected.int, E4.expected.int, E5.expected.int);
103}
104
105const T1 = union(E1) {
106 a: u8,
107 b: []const u16,
108 c: []const u8,
109 d: i32,
110 const expected = .{
111 .val = @unionInit(T1, @tagName(E1.expected.val), &.{ 1, 2, 3 }),
112 .int = E1.expected.int,
113 };
114};
115const T2 = union(E2) {
116 x,
117 y: i32,
118 z,
119 const expected = .{
120 .val = @unionInit(T2, @tagName(E2.expected.val), {}),
121 .int = E2.expected.int,
122 };
123};
124const T4 = union(E4) {
125 min: f32,
126 const expected = .{
127 .val = @unionInit(T4, @tagName(E4.expected.val), 0.123),
128 .int = E4.expected.int,
129 };
130};
131const T5 = union(E5) {
132 a: u0,
133 const expected = .{
134 .val = @unionInit(T5, @tagName(E5.expected.val), 0),
135 .int = E5.expected.int,
136 };
137};
138
139test "@backingInt with tagged unions" {
140 const static = struct {
141 fn doTheTest(v1: T1, v2: T2, v4: T4, v5: T5) !void {
142 const b1 = @backingInt(v1);
143 comptime assert(@TypeOf(b1) == @typeInfo(@typeInfo(T1).@"union".tag_type.?).@"enum".tag_type);
144 try expect(b1 == E1.expected.int);
145
146 const b2 = @backingInt(v2);
147 comptime assert(@TypeOf(b2) == @typeInfo(@typeInfo(T2).@"union".tag_type.?).@"enum".tag_type);
148 try expect(b2 == E2.expected.int);
149
150 const b4 = @backingInt(v4);
151 comptime assert(@TypeOf(b4) == @typeInfo(@typeInfo(T4).@"union".tag_type.?).@"enum".tag_type);
152 try expect(b4 == E4.expected.int);
153
154 const b5 = @backingInt(v5);
155 comptime assert(@TypeOf(b5) == @typeInfo(@typeInfo(T5).@"union".tag_type.?).@"enum".tag_type);
156 try expect(b5 == E5.expected.int);
157 }
158 };
159 try static.doTheTest(T1.expected.val, T2.expected.val, T4.expected.val, T5.expected.val);
160 try comptime static.doTheTest(T1.expected.val, T2.expected.val, T4.expected.val, T5.expected.val);
161}
162
163const S1 = packed struct(u8) {
164 a: u4,
165 b: i4,
166 const expected = .{
167 .val = @as(S1, .{ .a = 0b1000, .b = 0b0010 }),
168 .int = @as(@typeInfo(S1).@"struct".backing_integer.?, 0b0010_1000),
169 };
170};
171const S2 = packed struct(i20) {
172 a: u10,
173 b: enum(i10) { x, y, z },
174 const expected = .{
175 .val = @as(S2, .{ .a = 0b0011001100, .b = .z }),
176 .int = @as(@typeInfo(S2).@"struct".backing_integer.?, 0b0000000010_0011001100),
177 };
178};
179const S3 = packed struct(i32) {
180 a: packed struct(u12) {
181 x: u8,
182 y: i4,
183 },
184 b: packed union(i20) {
185 x: i20,
186 y: enum(u20) { u, v },
187 },
188 const expected = .{
189 .val = @as(S3, .{ .a = .{ .x = 0b10010001, .y = 0b0110 }, .b = .{ .y = .v } }),
190 .int = @as(@typeInfo(S3).@"struct".backing_integer.?, 0b00000000000000000001_0110_10010001),
191 };
192};
193const S4 = packed struct(i200) {
194 a: u200,
195 const expected = .{
196 .val = @as(S4, .{ .a = (1 << 199) + 10 }),
197 .int = @as(@typeInfo(S4).@"struct".backing_integer.?, @bitCast(@as(u200, (1 << 199) + 10))),
198 };
199};
200const S5 = packed struct(u0) {
201 a: u0,
202 const expected = .{
203 .val = @as(S5, .{ .a = 0 }),
204 .int = @as(@typeInfo(S5).@"struct".backing_integer.?, 0),
205 };
206};
207
208test "@backingInt with packed structs" {
209 const static = struct {
210 fn doTheTest(v1: S1, v2: S2, v3: S3, v4: S4, v5: S5) !void {
211 const b1 = @backingInt(v1);
212 comptime assert(@TypeOf(b1) == @typeInfo(S1).@"struct".backing_integer.?);
213 try expect(b1 == S1.expected.int);
214
215 const b2 = @backingInt(v2);
216 comptime assert(@TypeOf(b2) == @typeInfo(S2).@"struct".backing_integer.?);
217 try expect(b2 == S2.expected.int);
218
219 const b3 = @backingInt(v3);
220 comptime assert(@TypeOf(b3) == @typeInfo(S3).@"struct".backing_integer.?);
221 try expect(b3 == S3.expected.int);
222
223 const b4 = @backingInt(v4);
224 comptime assert(@TypeOf(b4) == @typeInfo(S4).@"struct".backing_integer.?);
225 try expect(b4 == S4.expected.int);
226
227 const b5 = @backingInt(v5);
228 comptime assert(@TypeOf(b5) == @typeInfo(S5).@"struct".backing_integer.?);
229 try expect(b5 == S5.expected.int);
230 }
231 };
232 try static.doTheTest(S1.expected.val, S2.expected.val, S3.expected.val, S4.expected.val, S5.expected.val);
233 try comptime static.doTheTest(S1.expected.val, S2.expected.val, S3.expected.val, S4.expected.val, S5.expected.val);
234}
235
236test "@fromBackingInt with packed structs" {
237 const static = struct {
238 fn doTheTest(
239 b1: @typeInfo(S1).@"struct".backing_integer.?,
240 b2: @typeInfo(S2).@"struct".backing_integer.?,
241 b3: @typeInfo(S3).@"struct".backing_integer.?,
242 b4: @typeInfo(S4).@"struct".backing_integer.?,
243 b5: @typeInfo(S5).@"struct".backing_integer.?,
244 ) !void {
245 const v1: S1 = @fromBackingInt(b1);
246 try expect(v1 == S1.expected.val);
247
248 const v2: S2 = @fromBackingInt(b2);
249 try expect(v2 == S2.expected.val);
250
251 const v3: S3 = @fromBackingInt(b3);
252 try expect(v3 == S3.expected.val);
253
254 const v4: S4 = @fromBackingInt(b4);
255 try expect(v4 == S4.expected.val);
256
257 const v5: S5 = @fromBackingInt(b5);
258 try expect(v5 == S5.expected.val);
259 }
260 };
261 try static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int);
262 try comptime static.doTheTest(S1.expected.int, S2.expected.int, S3.expected.int, S4.expected.int, S5.expected.int);
263}
264
265const U1 = packed union(u8) {
266 a: u8,
267 b: i8,
268 const expected = .{
269 .val = @as(U1, .{ .b = -123 }),
270 .int = @as(@typeInfo(U1).@"union".backing_integer.?, @bitCast(@as(i8, -123))),
271 };
272};
273const U2 = packed union(i20) {
274 a: u20,
275 b: enum(i20) { x, y, z },
276 const expected = .{
277 .val = @as(U2, .{ .b = .z }),
278 .int = @as(@typeInfo(U2).@"union".backing_integer.?, 0b00000000000000000000000000000010),
279 };
280};
281const U3 = packed union(i32) {
282 a: packed struct(u32) {
283 x: u18,
284 y: i14,
285 },
286 b: packed union(u32) {
287 x: i32,
288 y: enum(u32) { u, v },
289 },
290 const expected = .{
291 .val = @as(U3, .{ .b = .{ .y = .v } }),
292 .int = @as(@typeInfo(U3).@"union".backing_integer.?, 0b00000000000000000000000000000001),
293 };
294};
295const U4 = packed union(i200) {
296 a: u200,
297 const expected = .{
298 .val = @as(U4, .{ .a = (1 << 199) + 10 }),
299 .int = @as(@typeInfo(U4).@"union".backing_integer.?, @bitCast(@as(u200, (1 << 199) + 10))),
300 };
301};
302const U5 = packed union(u0) {
303 a: u0,
304 const expected = .{
305 .val = @as(U5, .{ .a = 0 }),
306 .int = @as(@typeInfo(U5).@"union".backing_integer.?, 0),
307 };
308};
309
310test "@backingInt with packed unions" {
311 const static = struct {
312 fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void {
313 const b1 = @backingInt(v1);
314 comptime assert(@TypeOf(b1) == @typeInfo(U1).@"union".backing_integer.?);
315 try expect(b1 == U1.expected.int);
316
317 const b2 = @backingInt(v2);
318 comptime assert(@TypeOf(b2) == @typeInfo(U2).@"union".backing_integer.?);
319 try expect(b2 == U2.expected.int);
320
321 const b3 = @backingInt(v3);
322 comptime assert(@TypeOf(b3) == @typeInfo(U3).@"union".backing_integer.?);
323 try expect(b3 == U3.expected.int);
324
325 const b4 = @backingInt(v4);
326 comptime assert(@TypeOf(b4) == @typeInfo(U4).@"union".backing_integer.?);
327 try expect(b4 == U4.expected.int);
328
329 const b5 = @backingInt(v5);
330 comptime assert(@TypeOf(b5) == @typeInfo(U5).@"union".backing_integer.?);
331 try expect(b5 == U5.expected.int);
332 }
333 };
334 try static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val);
335 try comptime static.doTheTest(U1.expected.val, U2.expected.val, U3.expected.val, U4.expected.val, U5.expected.val);
336}
337
338test "@fromBackingInt with packed unions" {
339 const static = struct {
340 fn doTheTest(
341 b1: @typeInfo(U1).@"union".backing_integer.?,
342 b2: @typeInfo(U2).@"union".backing_integer.?,
343 b3: @typeInfo(U3).@"union".backing_integer.?,
344 b4: @typeInfo(U4).@"union".backing_integer.?,
345 b5: @typeInfo(U5).@"union".backing_integer.?,
346 ) !void {
347 const v1: U1 = @fromBackingInt(b1);
348 try expect(v1 == U1.expected.val);
349
350 const v2: U2 = @fromBackingInt(b2);
351 try expect(v2 == U2.expected.val);
352
353 const v3: U3 = @fromBackingInt(b3);
354 try expect(v3 == U3.expected.val);
355
356 const v4: U4 = @fromBackingInt(b4);
357 try expect(v4 == U4.expected.val);
358
359 const v5: U5 = @fromBackingInt(b5);
360 try expect(v5 == U5.expected.val);
361 }
362 };
363 try static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int);
364 try comptime static.doTheTest(U1.expected.int, U2.expected.int, U3.expected.int, U4.expected.int, U5.expected.int);
365}
366
367test "@fromBackingInt provides result type to its argument" {
368 const E = enum(u32) { a, b, c };
369 const static = struct {
370 fn doTheTest(x: u8) !void {
371 const e: E = @fromBackingInt(x);
372 try expect(e == .b);
373 try expect(@backingInt(e) == x);
374 }
375 };
376 try static.doTheTest(1);
377 try comptime static.doTheTest(1);
378}
test/behavior/enum.zig+15
......@@ -1465,3 +1465,18 @@ test "enum int tag type uses declaration inside the enum" {
14651465 try expect(val == .b);
14661466 try expect(@intFromEnum(val) == 1);
14671467}
1468
1469test "convert from/to backing int" {
1470 const E = enum(u33) {
1471 a,
1472 b,
1473 c,
1474 fn doTheTest(s: @This()) !void {
1475 const backing_int = @backingInt(s);
1476 const reconstructed: @This() = @fromBackingInt(backing_int);
1477 try expect(reconstructed == s);
1478 }
1479 };
1480 try E.doTheTest(.b);
1481 try comptime E.doTheTest(.b);
1482}
test/behavior/packed-struct.zig+23-8
......@@ -122,7 +122,7 @@ test "correct sizeOf and offsets in packed structs" {
122122 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
123123 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
124124
125 const PStruct = packed struct {
125 const PStruct = packed struct(u32) {
126126 bool_a: bool,
127127 bool_b: bool,
128128 bool_c: bool,
......@@ -162,7 +162,7 @@ test "correct sizeOf and offsets in packed structs" {
162162 try expectEqual(22, @bitOffsetOf(PStruct, "u10_b"));
163163 try expectEqual(4, @sizeOf(PStruct));
164164
165 const s1 = @as(PStruct, @bitCast(@as(u32, 0x12345678)));
165 const s1: PStruct = @fromBackingInt(0x12345678);
166166 try expectEqual(false, s1.bool_a);
167167 try expectEqual(false, s1.bool_b);
168168 try expectEqual(false, s1.bool_c);
......@@ -176,7 +176,7 @@ test "correct sizeOf and offsets in packed structs" {
176176 try expectEqual(0b1101000101, s1.u10_a);
177177 try expectEqual(0b0001001000, s1.u10_b);
178178
179 const s2 = @as(packed struct { x: u1, y: u7, z: u24 }, @bitCast(@as(u32, 0xd5c71ff4)));
179 const s2: packed struct(u32) { x: u1, y: u7, z: u24 } = @fromBackingInt(0xd5c71ff4);
180180 try expectEqual(0, s2.x);
181181 try expectEqual(0b1111010, s2.y);
182182 try expectEqual(0xd5c71f, s2.z);
......@@ -191,7 +191,7 @@ test "nested packed structs" {
191191 const S2 = packed struct { d: u8, e: u8, f: u8 };
192192
193193 const S3 = packed struct { x: S1, y: S2 };
194 const S3Padded = packed struct { s3: S3, pad: u16 };
194 const S3Padded = packed struct(u64) { s3: S3, pad: u16 };
195195
196196 try expectEqual(48, @bitSizeOf(S3));
197197 try expectEqual(@sizeOf(u48), @sizeOf(S3));
......@@ -199,7 +199,7 @@ test "nested packed structs" {
199199 try expectEqual(3, @offsetOf(S3, "y"));
200200 try expectEqual(24, @bitOffsetOf(S3, "y"));
201201
202 const s3 = @as(S3Padded, @bitCast(@as(u64, 0xe952d5c71ff4))).s3;
202 const s3 = @as(S3Padded, @fromBackingInt(0xe952d5c71ff4)).s3;
203203 try expectEqual(0xf4, s3.x.a);
204204 try expectEqual(0x1f, s3.x.b);
205205 try expectEqual(0xc7, s3.x.c);
......@@ -558,7 +558,7 @@ test "packed struct fields modification" {
558558 // Originally reported at https://github.com/ziglang/zig/issues/16615
559559 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
560560
561 const Small = packed struct {
561 const Small = packed struct(u16) {
562562 val: u8 = 0,
563563 lo: u4 = 0,
564564 hi: u4 = 0,
......@@ -570,12 +570,12 @@ test "packed struct fields modification" {
570570 .lo = 3,
571571 .hi = 4,
572572 };
573 try expect(@as(u16, @bitCast(Small.p)) == 0x4312);
573 try expect(@backingInt(Small.p) == 0x4312);
574574
575575 Small.p.val -= Small.p.lo;
576576 Small.p.val += Small.p.hi;
577577 Small.p.hi -= Small.p.lo;
578 try expect(@as(u16, @bitCast(Small.p)) == 0x1313);
578 try expect(@backingInt(Small.p) == 0x1313);
579579}
580580
581581test "nested packed struct field access test" {
......@@ -1246,3 +1246,18 @@ test "initialize packed struct field to undefined at comptime" {
12461246 const val: S = .{ .x = undefined };
12471247 _ = val;
12481248}
1249
1250test "convert from/to backing int" {
1251 const S = packed struct(u33) {
1252 a: u7,
1253 b: enum(u10) { x, y, z },
1254 c: f16,
1255 fn doTheTest(s: @This()) !void {
1256 const backing_int = @backingInt(s);
1257 const reconstructed: @This() = @fromBackingInt(backing_int);
1258 try expect(reconstructed == s);
1259 }
1260 };
1261 try S.doTheTest(.{ .a = 123, .b = .y, .c = 0.23 });
1262 try comptime S.doTheTest(.{ .a = 123, .b = .y, .c = 0.23 });
1263}
test/behavior/packed-union.zig+14
......@@ -227,3 +227,17 @@ test "initialize packed union field to undefined at comptime" {
227227 const val: U = .{ .x = undefined };
228228 _ = val;
229229}
230
231test "convert from/to backing int" {
232 const U = packed union(u10) {
233 a: i10,
234 b: enum(u10) { x, y, z },
235 fn doTheTest(u: @This()) !void {
236 const backing_int = @backingInt(u);
237 const reconstructed: @This() = @fromBackingInt(backing_int);
238 try expect(reconstructed == u);
239 }
240 };
241 try U.doTheTest(.{ .a = 123 });
242 try comptime U.doTheTest(.{ .a = 123 });
243}
test/behavior/switch.zig+2-2
......@@ -1132,13 +1132,13 @@ test "decl literals as switch cases" {
11321132 try comptime E.doTheTest(.foo);
11331133}
11341134
1135// TODO audit after #15909 and/or #19855 are decided/implemented.
1135// TODO audit after https://github.com/ziglang/zig/issues/15909 is fully decided.
11361136// When we do that, consider adding an 'error{}' case if possible.
11371137test "switch with uninstantiable union fields" {
11381138 const U = union(enum) {
11391139 ok: void,
11401140 a: noreturn,
1141 b: noreturn,
1141 b: enum {},
11421142
11431143 fn doTheTest(u: @This()) void {
11441144 switch (u) {
test/behavior/type.zig+10-2
......@@ -266,13 +266,21 @@ test "Type.Union from regular enum" {
266266test "Type.Union from empty regular enum" {
267267 const E = enum {};
268268 const U = @Union(.auto, E, &.{}, &.{}, &.{});
269 try testing.expectEqual(@typeInfo(U).@"union".field_names.len, 0);
269
270 const info = @typeInfo(U).@"union";
271 try testing.expect(info.field_names.len == 0);
272 try testing.expect(info.tag_type != null);
273 try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn);
270274}
271275
272276test "Type.Union from empty Type.Enum" {
273277 const E = @Enum(noreturn, .exhaustive, &.{}, &.{});
274278 const U = @Union(.auto, E, &.{}, &.{}, &.{});
275 try testing.expectEqual(@typeInfo(U).@"union".field_names.len, 0);
279
280 const info = @typeInfo(U).@"union";
281 try testing.expect(info.field_names.len == 0);
282 try testing.expect(info.tag_type != null);
283 try testing.expect(@typeInfo(info.tag_type.?).@"enum".tag_type == noreturn);
276284}
277285
278286test "Type.Fn" {
test/cases/compile_errors/backing_int_invalid_arg_type.zig created+38
......@@ -0,0 +1,38 @@
1const S1 = extern struct { x: u32 };
2export fn entry1(x: S1) u32 {
3 return @backingInt(x);
4}
5
6const U1 = extern union { x: u32 };
7export fn entry2(x: U1) u32 {
8 return @backingInt(x);
9}
10
11export fn entry3(x: u32) u32 {
12 return @backingInt(x);
13}
14
15const S2 = packed struct { x: u32 };
16export fn entry4(x: u32) u32 {
17 return @backingInt(@as(S2, .{ .x = x }));
18}
19
20const U2 = packed union { x: u32 };
21export fn entry5(x: u32) u32 {
22 return @backingInt(@as(U2, .{ .x = x }));
23}
24
25// error
26//
27// :3:24: error: non-packed struct 'tmp.S1' does not have a backing integer
28// :1:19: note: struct declared here
29// :8:24: error: non-packed union 'tmp.U1' does not have a backing integer
30// :8:24: note: untagged union 'tmp.U1' does not have an enum tag with a backing integer
31// :6:19: note: union declared here
32// :12:24: error: expected enum, tagged union, packed union or packed struct, found 'u32'
33// :17:24: error: @backingInt is ambiguous for type 'tmp.S2'
34// :15:19: note: backing integer type of struct is inferred
35// :15:19: note: consider explicitly specifying the backing integer type
36// :22:24: error: @backingInt is ambiguous for type 'tmp.U2'
37// :20:19: note: backing integer type of union is inferred
38// :20:19: note: consider explicitly specifying the backing integer type
test/cases/compile_errors/bitCast_with_invalid_array_element_type.zig deleted-23
......@@ -1,23 +0,0 @@
1export fn foo() void {
2 const S = struct {
3 f: u8,
4 };
5 _ = @as([@sizeOf(S)]u8, @bitCast([1]S{undefined}));
6}
7
8export fn bar() void {
9 const S = struct {
10 f: u8,
11 };
12 _ = @as([1]S, @bitCast(@as([@sizeOf(S)]u8, undefined)));
13}
14
15export fn baz() void {
16 _ = @as([1]u32, @bitCast([1]comptime_int{0}));
17}
18
19// error
20//
21// :5:42: error: cannot @bitCast from '[1]tmp.foo.S'
22// :12:19: error: cannot @bitCast to '[1]tmp.bar.S'
23// :16:45: error: cannot @bitCast from '[1]comptime_int'
test/cases/compile_errors/bitcast_invalid_types.zig created+205
......@@ -0,0 +1,205 @@
1const y: u32 = 0;
2
3export fn entry1() void {
4 _ = @as(comptime_float, @bitCast(y));
5}
6export fn entry2() void {
7 const x: comptime_float = undefined;
8 _ = @as(u32, @bitCast(x));
9}
10
11export fn entry3() void {
12 _ = @as(comptime_int, @bitCast(y));
13}
14export fn entry4() void {
15 const x: comptime_int = undefined;
16 _ = @as(u32, @bitCast(x));
17}
18
19export fn entry5() void {
20 _ = @as(@EnumLiteral(), @bitCast(y));
21}
22export fn entry6() void {
23 const x: @EnumLiteral() = undefined;
24 _ = @as(u32, @bitCast(x));
25}
26
27export fn entry7() void {
28 _ = @as(error{}, @bitCast(y));
29}
30export fn entry8() void {
31 const x: error{} = undefined;
32 _ = @as(u32, @bitCast(x));
33}
34
35export fn entry9() void {
36 _ = @as(?(anyerror!u32), @bitCast(y));
37}
38export fn entry10() void {
39 const x: anyerror!u32 = undefined;
40 _ = @as(u32, @bitCast(x));
41}
42
43export fn entry11() void {
44 _ = @as(fn () void, @bitCast(y));
45}
46export fn entry12() void {
47 const x: fn () void = undefined;
48 _ = @as(u32, @bitCast(x));
49}
50
51export fn entry13() void {
52 _ = @as(noreturn, @bitCast(y));
53}
54
55export fn entry14() void {
56 _ = @as(@TypeOf(null), @bitCast(y));
57}
58export fn entry15() void {
59 const x: @TypeOf(null) = undefined;
60 _ = @as(u32, @bitCast(x));
61}
62
63export fn entry16() void {
64 const O = opaque {};
65 _ = @as(O, @bitCast(y));
66}
67
68export fn entry17() void {
69 _ = @as(??u32, @bitCast(y));
70}
71export fn entry18() void {
72 const x: ?u32 = undefined;
73 _ = @as(u32, @bitCast(x));
74}
75
76export fn entry19() void {
77 _ = @as(type, @bitCast(y));
78}
79export fn entry20() void {
80 const x: type = undefined;
81 _ = @as(u32, @bitCast(x));
82}
83
84export fn entry21() void {
85 _ = @as(@TypeOf(undefined), @bitCast(y));
86}
87export fn entry22() void {
88 const x: @TypeOf(undefined) = undefined;
89 _ = @as(u32, @bitCast(x));
90}
91
92export fn entry23() void {
93 _ = @as(void, @bitCast(y));
94}
95export fn entry24() void {
96 const x: void = undefined;
97 _ = @as(u32, @bitCast(x));
98}
99
100export fn entry25() void {
101 _ = @as(*u8, @bitCast(y));
102}
103export fn entry26() void {
104 const x: *u8 = undefined;
105 _ = @as(u32, @bitCast(x));
106}
107
108export fn entry27() void {
109 _ = @as(*u8, @bitCast(@as(*u32, @ptrFromInt(y))));
110}
111export fn entry28() void {
112 const x: *u8 = undefined;
113 _ = @as(*u32, @bitCast(x));
114}
115
116export fn entry29() void {
117 const S = struct { x: u32 };
118 _ = @as(S, @bitCast(y));
119}
120export fn entry30() void {
121 const S = struct { x: u32 };
122 const x: S = undefined;
123 _ = @as(u32, @bitCast(x));
124}
125
126export fn entry31() void {
127 const U = union { x: u32 };
128 _ = @as(U, @bitCast(y));
129}
130export fn entry32() void {
131 const U = union { x: u32 };
132 const x: U = undefined;
133 _ = @as(u32, @bitCast(x));
134}
135
136export fn entry33() void {
137 const S = struct { x: u32 };
138 _ = @as([10]S, @bitCast(y));
139}
140export fn entry34() void {
141 const S = struct { x: u32 };
142 const x: [10]S = undefined;
143 _ = @as(u32, @bitCast(x));
144}
145
146export fn entry35() void {
147 const E = enum {};
148 _ = @as(E, @bitCast(y));
149}
150
151export fn entry36() void {
152 const E = enum { a, b, c };
153 _ = @as(E, @bitCast(y));
154}
155
156// error
157//
158// :4:29: error: cannot @bitCast to 'comptime_float'
159// :8:27: error: cannot @bitCast from 'comptime_float'
160// :12:27: error: cannot @bitCast to 'comptime_int'
161// :16:27: error: cannot @bitCast from 'comptime_int'
162// :20:29: error: cannot @bitCast to '@EnumLiteral()'
163// :24:27: error: cannot @bitCast from '@EnumLiteral()'
164// :28:22: error: cannot @bitCast to 'error{}'
165// :32:27: error: cannot @bitCast from 'error{}'
166// :36:30: error: cannot @bitCast to 'anyerror!u32'
167// :40:27: error: cannot @bitCast from 'anyerror!u32'
168// :44:25: error: cannot @bitCast to 'fn () void'
169// :48:27: error: cannot @bitCast from 'fn () void'
170// :52:23: error: cannot @bitCast to 'noreturn'
171// :56:28: error: cannot @bitCast to '@TypeOf(null)'
172// :60:27: error: cannot @bitCast from '@TypeOf(null)'
173// :65:16: error: cannot @bitCast to 'tmp.entry16.O'
174// :64:15: note: opaque declared here
175// :69:20: error: cannot @bitCast to '?u32'
176// :69:20: note: use @ptrFromInt to cast from 'u32'
177// :73:27: error: cannot @bitCast from '?u32'
178// :73:27: note: use @intFromPtr to cast to 'u32'
179// :77:19: error: cannot @bitCast to 'type'
180// :81:27: error: cannot @bitCast from 'type'
181// :85:33: error: cannot @bitCast to '@TypeOf(undefined)'
182// :89:27: error: cannot @bitCast from '@TypeOf(undefined)'
183// :93:19: error: @bitCast size mismatch: destination type 'void' has 0 bits but source type 'u32' has 32 bits
184// :97:18: error: @bitCast size mismatch: destination type 'u32' has 32 bits but source type 'void' has 0 bits
185// :101:18: error: cannot @bitCast to '*u8'
186// :101:18: note: use @ptrFromInt to cast from 'u32'
187// :105:27: error: cannot @bitCast from '*u8'
188// :105:27: note: use @intFromPtr to cast to 'u32'
189// :109:49: error: pointer type '*u32' does not allow address zero
190// :113:19: error: cannot @bitCast to '*u32'
191// :113:19: note: use @ptrCast to cast from '*u8'
192// :118:16: error: cannot @bitCast to 'tmp.entry29.S'
193// :117:15: note: struct declared here
194// :123:27: error: cannot @bitCast from 'tmp.entry30.S'
195// :121:15: note: struct declared here
196// :128:16: error: cannot @bitCast to 'tmp.entry31.U'
197// :127:15: note: union declared here
198// :133:27: error: cannot @bitCast from 'tmp.entry32.U'
199// :131:15: note: union declared here
200// :138:20: error: cannot @bitCast to '[10]tmp.entry33.S'
201// :143:27: error: cannot @bitCast from '[10]tmp.entry34.S'
202// :148:16: error: cannot @bitCast to 'tmp.entry35.E'
203// :147:15: note: enum declared here
204// :153:16: error: cannot @bitCast to 'tmp.entry36.E'
205// :152:15: note: enum declared here
test/cases/compile_errors/bitcast_to_enum_invalid_tag_value.zig created+16
......@@ -0,0 +1,16 @@
1const E = enum(u8) { a, b, c };
2export fn entry1() void {
3 const x: E = @bitCast(@as(u8, 3));
4 _ = x;
5}
6
7export fn entry2() void {
8 const x: E = @bitCast(@as(u8, undefined));
9 _ = x;
10}
11
12// error
13//
14// :3:18: error: enum 'tmp.E' has no tag with value '3'
15// :1:11: note: enum declared here
16// :8:27: error: use of undefined value here causes illegal behavior
test/cases/compile_errors/empty_enum_from_backing_int.zig created+18
......@@ -0,0 +1,18 @@
1const E = enum(noreturn) {};
2
3export fn entry1() void {
4 const e: E = @fromBackingInt(undefined);
5 _ = e;
6}
7
8export fn entry2() void {
9 const e: E = @fromBackingInt(0);
10 _ = e;
11}
12
13// error
14//
15// :4:34: error: expected type 'noreturn', found '@TypeOf(undefined)'
16// :4:34: note: cannot coerce to uninstantiable type 'noreturn'
17// :9:34: error: expected type 'noreturn', found 'comptime_int'
18// :9:34: note: cannot coerce to uninstantiable type 'noreturn'
test/cases/compile_errors/from_backing_int_invalid_dest_type.zig created+37
......@@ -0,0 +1,37 @@
1const S1 = extern struct { x: u32 };
2export fn entry1(x: u32) void {
3 _ = @as(S1, @fromBackingInt(x));
4}
5
6const U1 = extern union { x: u32 };
7export fn entry2(x: u32) void {
8 _ = @as(U1, @fromBackingInt(x));
9}
10
11export fn entry3(x: u32) void {
12 _ = @as(u32, @fromBackingInt(x));
13}
14
15const S2 = packed struct { x: u32 };
16export fn entry4(x: u32) void {
17 _ = @as(S2, @fromBackingInt(x));
18}
19
20const U2 = packed union { x: u32 };
21export fn entry5(x: u32) u32 {
22 _ = @as(U2, @fromBackingInt(x));
23}
24
25// error
26//
27// :3:17: error: non-packed struct 'tmp.S1' does not have a backing integer
28// :1:19: note: struct declared here
29// :8:17: error: non-packed union 'tmp.U1' does not have a backing integer
30// :6:19: note: union declared here
31// :12:18: error: expected enum, packed union or packed struct, found 'u32'
32// :17:17: error: @fromBackingInt is ambiguous for type 'tmp.S2'
33// :15:19: note: backing integer type of struct is inferred
34// :15:19: note: consider explicitly specifying the backing integer type
35// :22:17: error: @fromBackingInt is ambiguous for type 'tmp.U2'
36// :20:19: note: backing integer type of union is inferred
37// :20:19: note: consider explicitly specifying the backing integer type
test/cases/compile_errors/from_backing_int_type_mismatch.zig created+23
......@@ -0,0 +1,23 @@
1const E = enum(u32) { a, b, c };
2export fn entry1(x: u64) E {
3 return @fromBackingInt(x);
4}
5
6const S = packed struct(u32) { x: u32 };
7export fn entry2(x: u64) S {
8 return @fromBackingInt(x);
9}
10
11const U = packed union(u32) { x: u32 };
12export fn entry3(x: u64) U {
13 return @fromBackingInt(x);
14}
15
16// error
17//
18// :3:28: error: expected type 'u32', found 'u64'
19// :3:28: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
20// :8:28: error: expected type 'u32', found 'u64'
21// :8:28: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
22// :13:28: error: expected type 'u32', found 'u64'
23// :13:28: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
test/cases/compile_errors/from_backing_int_undef.zig created+22
......@@ -0,0 +1,22 @@
1const E = enum(u32) { x };
2export fn entry1() void {
3 @compileLog(@as(E, @fromBackingInt(undefined)));
4}
5
6const S = packed struct(u32) { x: u32 };
7export fn entry2() void {
8 @compileLog(@as(S, @fromBackingInt(undefined)));
9}
10
11const U = packed union(u32) { x: u32 };
12export fn entry3() void {
13 @compileLog(@as(U, @fromBackingInt(undefined)));
14}
15
16// error
17//
18// :3:40: error: use of undefined value here causes illegal behavior
19//
20// Compile Log Output:
21// @as(tmp.S, undefined)
22// @as(tmp.U, undefined)
test/cases/compile_errors/invalid_non-exhaustive_enum_to_union.zig+1-1
......@@ -22,5 +22,5 @@ export fn bar() void {
2222//
2323// :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum
2424// :1:11: note: enum declared here
25// :17:16: error: union 'tmp.U' has no tag with value '@enumFromInt(15)'
25// :17:16: error: union 'tmp.U' has no tag with value '@fromBackingInt(15)'
2626// :6:11: note: union declared here
test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig+1-1
......@@ -6,5 +6,5 @@ test "enum" {
66// error
77// is_test=true
88//
9// :3:9: error: no field with value '@enumFromInt(5)' in enum 'tmp.test.enum.E'
9// :3:9: error: no field with value '@fromBackingInt(5)' in enum 'tmp.test.enum.E'
1010// :2:15: note: declared here
test/cases/safety/backing_int_no_matching_tag_value.zig created+25
......@@ -0,0 +1,25 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid enum value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const Foo = enum(u8) {
11 a,
12 b,
13 c,
14};
15pub fn main() !void {
16 _ = bar(3);
17 return error.TestFailed;
18}
19fn bar(a: u8) Foo {
20 return @fromBackingInt(a);
21}
22
23// run
24// backend=selfhosted,llvm
25// target=x86_64-linux,aarch64-linux
test/cases/safety/bitcast_to_enum_no_matching_tag_value.zig created+25
......@@ -0,0 +1,25 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid enum value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const Foo = enum(u8) {
11 a,
12 b,
13 c,
14};
15pub fn main() !void {
16 _ = bar(3);
17 return error.TestFailed;
18}
19fn bar(a: u8) Foo {
20 return @bitCast(a);
21}
22
23// run
24// backend=selfhosted,llvm
25// target=x86_64-linux,aarch64-linux