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 {...@@ -888,7 +888,7 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
888/// Given a type and value, cast the value to the type as c would.888/// Given a type and value, cast the value to the type as c would.
889/// This is for translate-c and is not intended for general use.889/// This is for translate-c and is not intended for general use.
890pub fn cast(comptime DestType: type, target: anytype) DestType {890pub fn cast(comptime DestType: type, target: anytype) DestType {
891 // this function should behave like transCCast in translate-c, except it's for macros891 // this function should behave like transCCast in translate-c, except it's for macros and enums
892 const SourceType = @TypeOf(target);892 const SourceType = @TypeOf(target);
893 switch (@typeInfo(DestType)) {893 switch (@typeInfo(DestType)) {
894 .Pointer => {894 .Pointer => {
...@@ -925,9 +925,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -925,9 +925,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
925 }925 }
926 }926 }
927 },927 },
928 .Enum => {928 .Enum => |enum_type| {
929 if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {929 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);
931 }932 }
932 },933 },
933 .Int => {934 .Int => {
...@@ -1015,6 +1016,17 @@ test "std.meta.cast" {...@@ -1015,6 +1016,17 @@ test "std.meta.cast" {
1015 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));1016 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
10161017
1017 testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*u8, 2)));1018 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));
1018}1030}
10191031
1020/// Given a value returns its size as C's sizeof operator would.1032/// Given a value returns its size as C's sizeof operator would.
src/translate_c.zig+2-2
...@@ -2161,8 +2161,8 @@ fn transCCast(...@@ -2161,8 +2161,8 @@ fn transCCast(
2161 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });2161 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });
2162 }2162 }
2163 if (cIsEnum(dst_type)) {2163 if (cIsEnum(dst_type)) {
2164 // @intToEnum(dest_type, val)2164 // import("std").meta.cast(dest_type, val)
2165 return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr });2165 return Tag.std_meta_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
2166 }2166 }
2167 if (cIsEnum(src_type) and !cIsEnum(dst_type)) {2167 if (cIsEnum(src_type) and !cIsEnum(dst_type)) {
2168 // @enumToInt(val)2168 // @enumToInt(val)
test/run_translated_c.zig+14
...@@ -1453,4 +1453,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1453,4 +1453,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1453 \\ return 0;1453 \\ return 0;
1454 \\}1454 \\}
1455 , "");1455 , "");
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 , "");
1456}1470}
test/translate_c.zig+4-4
...@@ -111,7 +111,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -111,7 +111,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
111 \\ const A = @enumToInt(enum_Foo.A);111 \\ const A = @enumToInt(enum_Foo.A);
112 \\ const B = @enumToInt(enum_Foo.B);112 \\ const B = @enumToInt(enum_Foo.B);
113 \\ const C = @enumToInt(enum_Foo.C);113 \\ 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);
115 \\ {115 \\ {
116 \\ const enum_Foo = extern enum(c_int) {116 \\ const enum_Foo = extern enum(c_int) {
117 \\ A,117 \\ A,
...@@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
122 \\ const A_2 = @enumToInt(enum_Foo.A);122 \\ const A_2 = @enumToInt(enum_Foo.A);
123 \\ const B_3 = @enumToInt(enum_Foo.B);123 \\ const B_3 = @enumToInt(enum_Foo.B);
124 \\ const C_4 = @enumToInt(enum_Foo.C);124 \\ 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);
126 \\ }126 \\ }
127 \\}127 \\}
128 });128 });
...@@ -1676,7 +1676,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1676,7 +1676,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1676 \\pub const e = @enumToInt(enum_unnamed_1.e);1676 \\pub const e = @enumToInt(enum_unnamed_1.e);
1677 \\pub const f = @enumToInt(enum_unnamed_1.f);1677 \\pub const f = @enumToInt(enum_unnamed_1.f);
1678 \\pub const g = @enumToInt(enum_unnamed_1.g);1678 \\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);
1680 \\const enum_unnamed_2 = extern enum(c_int) {1680 \\const enum_unnamed_2 = extern enum(c_int) {
1681 \\ i,1681 \\ i,
1682 \\ j,1682 \\ j,
...@@ -2308,7 +2308,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2308,7 +2308,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2308 \\ var a = arg_a;2308 \\ var a = arg_a;
2309 \\ var b = arg_b;2309 \\ var b = arg_b;
2310 \\ var c = arg_c;2310 \\ 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);
2312 \\ var e: c_int = @boolToInt((a != 0) and (b != 0));2312 \\ var e: c_int = @boolToInt((a != 0) and (b != 0));
2313 \\ var f: c_int = @boolToInt((b != 0) and (c != null));2313 \\ var f: c_int = @boolToInt((b != 0) and (c != null));
2314 \\ var g: c_int = @boolToInt((a != 0) and (c != null));2314 \\ var g: c_int = @boolToInt((a != 0) and (c != null));