diff --git a/src/translate_c.zig b/src/translate_c.zig index 424691513a..7469c6e996 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1993,6 +1993,21 @@ fn transStringLiteral( } } +fn cIsEnum(qt: clang.QualType) bool { + return qt.getCanonicalType().getTypeClass() == .Enum; +} + +/// Get the underlying int type of an enum. The C compiler chooses a signed int +/// type that is large enough to hold all of the enum's values. It is not required +/// to be the smallest possible type that can hold all the values. +fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType { + assert(cIsEnum(enum_qt)); + const ty = enum_qt.getCanonicalType().getTypePtr(); + const enum_ty = @ptrCast(*const clang.EnumType, ty); + const enum_decl = enum_ty.getDecl(); + return enum_decl.getIntegerType(); +} + fn transCCast( rp: RestorePoint, scope: *Scope, @@ -2005,40 +2020,45 @@ fn transCCast( if (dst_type.eq(src_type)) return expr; if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) return transCPtrCast(rp, loc, dst_type, src_type, expr); - if (cIsInteger(dst_type) and cIsInteger(src_type)) { - // 1. Extend or truncate without changing signed-ness. - // 2. Bit-cast to correct signed-ness + if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) { + // 1. If src_type is an enum, determine the underlying signed int type + // 2. Extend or truncate without changing signed-ness. + // 3. Bit-cast to correct signed-ness + + const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type); + const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type); + const src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr); // @bitCast(dest_type, intermediate_value) const cast_node = try rp.c.createBuiltinCall("@bitCast", 2); cast_node.params()[0] = try transQualType(rp, dst_type, loc); _ = try appendToken(rp.c, .Comma, ","); - switch (cIntTypeCmp(dst_type, src_type)) { + switch (cIntTypeCmp(dst_type, src_int_type)) { .lt => { - // @truncate(SameSignSmallerInt, src_type) + // @truncate(SameSignSmallerInt, src_int_expr) const trunc_node = try rp.c.createBuiltinCall("@truncate", 2); - const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type)); + const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed); trunc_node.params()[0] = ty_node; _ = try appendToken(rp.c, .Comma, ","); - trunc_node.params()[1] = expr; + trunc_node.params()[1] = src_int_expr; trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); cast_node.params()[1] = &trunc_node.base; }, .gt => { - // @as(SameSignBiggerInt, src_type) + // @as(SameSignBiggerInt, src_int_expr) const as_node = try rp.c.createBuiltinCall("@as", 2); - const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type)); + const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed); as_node.params()[0] = ty_node; _ = try appendToken(rp.c, .Comma, ","); - as_node.params()[1] = expr; + as_node.params()[1] = src_int_expr; as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); cast_node.params()[1] = &as_node.base; }, .eq => { - cast_node.params()[1] = expr; + cast_node.params()[1] = src_int_expr; }, } cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); @@ -2119,7 +2139,7 @@ fn transCCast( return &cast_node.base; } - if (dst_type.getCanonicalType().getTypeClass() == .Enum) { + if (cIsEnum(dst_type)) { const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2); builtin_node.params()[0] = try transQualType(rp, dst_type, loc); _ = try appendToken(rp.c, .Comma, ","); @@ -2127,13 +2147,8 @@ fn transCCast( builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &builtin_node.base; } - if (src_type.getCanonicalType().getTypeClass() == .Enum and - dst_type.getCanonicalType().getTypeClass() != .Enum) - { - const builtin_node = try rp.c.createBuiltinCall("@enumToInt", 1); - builtin_node.params()[0] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &builtin_node.base; + if (cIsEnum(src_type) and !cIsEnum(dst_type)) { + return transEnumToInt(rp.c, expr); } const cast_node = try rp.c.createBuiltinCall("@as", 2); cast_node.params()[0] = try transQualType(rp, dst_type, loc); @@ -2143,6 +2158,13 @@ fn transCCast( return &cast_node.base; } +fn transEnumToInt(c: *Context, enum_expr: *ast.Node) TypeError!*ast.Node { + const builtin_node = try c.createBuiltinCall("@enumToInt", 1); + builtin_node.params()[0] = enum_expr; + builtin_node.rparen_token = try appendToken(c, .RParen, ")"); + return &builtin_node.base; +} + fn transExpr( rp: RestorePoint, scope: *Scope, diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 35f15eed4c..eb0b7db50a 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -371,4 +371,117 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("assign enum to uint, no explicit cast", + \\#include + \\typedef enum { + \\ ENUM_0 = 0, + \\ ENUM_1 = 1, + \\} my_enum_t; + \\ + \\int main() { + \\ my_enum_t val = ENUM_1; + \\ unsigned int x = val; + \\ if (x != 1) abort(); + \\ return 0; + \\} + , ""); + + cases.add("assign enum to int", + \\#include + \\typedef enum { + \\ ENUM_0 = 0, + \\ ENUM_1 = 1, + \\} my_enum_t; + \\ + \\int main() { + \\ my_enum_t val = ENUM_1; + \\ int x = val; + \\ if (x != 1) abort(); + \\ return 0; + \\} + , ""); + + cases.add("cast enum to smaller uint", + \\#include + \\#include + \\typedef enum { + \\ ENUM_0 = 0, + \\ ENUM_257 = 257, + \\} my_enum_t; + \\ + \\int main() { + \\ my_enum_t val = ENUM_257; + \\ uint8_t x = (uint8_t)val; + \\ if (x != (uint8_t)257) abort(); + \\ return 0; + \\} + , ""); + + cases.add("cast enum to smaller signed int", + \\#include + \\#include + \\typedef enum { + \\ ENUM_0 = 0, + \\ ENUM_384 = 384, + \\} my_enum_t; + \\ + \\int main() { + \\ my_enum_t val = ENUM_384; + \\ int8_t x = (int8_t)val; + \\ if (x != (int8_t)384) abort(); + \\ return 0; + \\} + , ""); + + cases.add("cast negative enum to smaller signed int", + \\#include + \\#include + \\typedef enum { + \\ ENUM_MINUS_1 = -1, + \\ ENUM_384 = 384, + \\} my_enum_t; + \\ + \\int main() { + \\ my_enum_t val = ENUM_MINUS_1; + \\ int8_t x = (int8_t)val; + \\ if (x != -1) abort(); + \\ return 0; + \\} + , ""); + + cases.add("cast negative enum to smaller unsigned int", + \\#include + \\#include + \\typedef enum { + \\ ENUM_MINUS_1 = -1, + \\ ENUM_384 = 384, + \\} my_enum_t; + \\ + \\int main() { + \\ my_enum_t val = ENUM_MINUS_1; + \\ uint8_t x = (uint8_t)val; + \\ if (x != (uint8_t)-1) abort(); + \\ return 0; + \\} + , ""); + + cases.add("implicit enum cast in boolean expression", + \\#include + \\enum Foo { + \\ FooA, + \\ FooB, + \\ FooC, + \\}; + \\int main() { + \\ int a = 0; + \\ float b = 0; + \\ void *c = 0; + \\ enum Foo d = FooA; + \\ if (a || d) abort(); + \\ if (d && b) abort(); + \\ if (c || d) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index afe7d1d9f5..676ea89c6e 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -2000,9 +2000,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ int h = (a || b); \\ int i = (b || c); \\ int j = (a || c); - \\ int k = (a || d); - \\ int l = (d && b); - \\ int m = (c || d); + \\ int k = (a || (int)d); + \\ int l = ((int)d && b); + \\ int m = (c || (unsigned int)d); \\ SomeTypedef td = 44; \\ int o = (td || b); \\ int p = (c && td); @@ -2027,9 +2027,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var h: c_int = @boolToInt(((a != 0) or (b != 0))); \\ var i: c_int = @boolToInt(((b != 0) or (c != null))); \\ var j: c_int = @boolToInt(((a != 0) or (c != null))); - \\ var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0))); - \\ var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0))); - \\ var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0))); + \\ var k: c_int = @boolToInt(((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0))); + \\ var l: c_int = @boolToInt(((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0))); + \\ var m: c_int = @boolToInt(((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0))); \\ var td: SomeTypedef = 44; \\ var o: c_int = @boolToInt(((td != 0) or (b != 0))); \\ var p: c_int = @boolToInt(((c != null) and (td != 0)));