authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-04-13 20:57:50-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-15 22:46:22-04:00
logd4d21dd46d69961300cc796abdebe352dfe6eae8
treeaa0e3483f5536d0d8b74894c78e373b045ca3f1b
parentccdf55310bee2dcf86b718d26a56933dc1a03443

translate-c: better handling of int -> enum casts

In std.meta.cast when casting to an enum type from an integer type, first do a C-style cast from the source value to the tag type of the enum. This ensures that we don't get an error due to the source value not being representable by the enum. In transCCast() use std.meta.cast instead of directly emitting the cast operation since the enum's underlying type may not be known at translation time due to an MSVC bug, see https://github.com/ziglang/zig/issues/8003 Fixes #6011

4 files changed, 35 insertions(+), 9 deletions(-)

lib/std/meta.zig+15-3
......@@ -888,7 +888,7 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
888888/// Given a type and value, cast the value to the type as c would.
889889/// This is for translate-c and is not intended for general use.
890890pub fn cast(comptime DestType: type, target: anytype) DestType {
891 // this function should behave like transCCast in translate-c, except it's for macros
891 // this function should behave like transCCast in translate-c, except it's for macros and enums
892892 const SourceType = @TypeOf(target);
893893 switch (@typeInfo(DestType)) {
894894 .Pointer => {
......@@ -925,9 +925,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
925925 }
926926 }
927927 },
928 .Enum => {
928 .Enum => |enum_type| {
929929 if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
930 return @intToEnum(DestType, target);
930 const intermediate = cast(enum_type.tag_type, target);
931 return @intToEnum(DestType, intermediate);
931932 }
932933 },
933934 .Int => {
......@@ -1015,6 +1016,17 @@ test "std.meta.cast" {
10151016 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
10161017
10171018 testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*u8, 2)));
1019
1020 const C_ENUM = extern enum(c_int) {
1021 A = 0,
1022 B,
1023 C,
1024 _,
1025 };
1026 testing.expectEqual(cast(C_ENUM, @as(i64, -1)), @intToEnum(C_ENUM, -1));
1027 testing.expectEqual(cast(C_ENUM, @as(i8, 1)), .B);
1028 testing.expectEqual(cast(C_ENUM, @as(u64, 1)), .B);
1029 testing.expectEqual(cast(C_ENUM, @as(u64, 42)), @intToEnum(C_ENUM, 42));
10181030}
10191031
10201032/// Given a value returns its size as C's sizeof operator would.
src/translate_c.zig+2-2
......@@ -2161,8 +2161,8 @@ fn transCCast(
21612161 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });
21622162 }
21632163 if (cIsEnum(dst_type)) {
2164 // @intToEnum(dest_type, val)
2165 return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
2164 // import("std").meta.cast(dest_type, val)
2165 return Tag.std_meta_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
21662166 }
21672167 if (cIsEnum(src_type) and !cIsEnum(dst_type)) {
21682168 // @enumToInt(val)
test/run_translated_c.zig+14
......@@ -1453,4 +1453,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
14531453 \\ return 0;
14541454 \\}
14551455 , "");
1456
1457 cases.add("Cast to enum from larger integral type. Issue #6011",
1458 \\#include <stdint.h>
1459 \\#include <stdlib.h>
1460 \\enum Foo { A, B, C };
1461 \\static inline enum Foo do_stuff(void) {
1462 \\ int64_t i = 1;
1463 \\ return (enum Foo)i;
1464 \\}
1465 \\int main(void) {
1466 \\ if (do_stuff() != B) abort();
1467 \\ return 0;
1468 \\}
1469 , "");
14561470}
test/translate_c.zig+4-4
......@@ -111,7 +111,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
111111 \\ const A = @enumToInt(enum_Foo.A);
112112 \\ const B = @enumToInt(enum_Foo.B);
113113 \\ const C = @enumToInt(enum_Foo.C);
114 \\ var a: enum_Foo = @intToEnum(enum_Foo, B);
114 \\ var a: enum_Foo = @import("std").meta.cast(enum_Foo, B);
115115 \\ {
116116 \\ const enum_Foo = extern enum(c_int) {
117117 \\ A,
......@@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
122122 \\ const A_2 = @enumToInt(enum_Foo.A);
123123 \\ const B_3 = @enumToInt(enum_Foo.B);
124124 \\ const C_4 = @enumToInt(enum_Foo.C);
125 \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3);
125 \\ var a_5: enum_Foo = @import("std").meta.cast(enum_Foo, B_3);
126126 \\ }
127127 \\}
128128 });
......@@ -1676,7 +1676,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16761676 \\pub const e = @enumToInt(enum_unnamed_1.e);
16771677 \\pub const f = @enumToInt(enum_unnamed_1.f);
16781678 \\pub const g = @enumToInt(enum_unnamed_1.g);
1679 \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e);
1679 \\pub export var h: enum_unnamed_1 = @import("std").meta.cast(enum_unnamed_1, e);
16801680 \\const enum_unnamed_2 = extern enum(c_int) {
16811681 \\ i,
16821682 \\ j,
......@@ -2308,7 +2308,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
23082308 \\ var a = arg_a;
23092309 \\ var b = arg_b;
23102310 \\ var c = arg_c;
2311 \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA);
2311 \\ var d: enum_Foo = @import("std").meta.cast(enum_Foo, FooA);
23122312 \\ var e: c_int = @boolToInt((a != 0) and (b != 0));
23132313 \\ var f: c_int = @boolToInt((b != 0) and (c != null));
23142314 \\ var g: c_int = @boolToInt((a != 0) and (c != null));