commit 4fc6f631e044b5ddfff6c610f04f3619a1bbeb8d (tree)
parent bdb917006c9920f2a0d2091cb0f3d52454e039f0
Author: Andrew Kelley <andrew@ziglang.org>
Date: Thu, 11 Mar 2021 14:32:37 -0500
Merge pull request #8126 from xackus/translate_c_int_literal_promotion
translate-c: promote int literals to bigger types
Diffstat:
4 files changed, 178 insertions(+), 49 deletions(-)
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
@@ -1094,6 +1094,58 @@ test "sizeof" {
testing.expect(sizeof(c_void) == 1);
}
+pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal };
+
+fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime radix: CIntLiteralRadix) type {
+ const signed_decimal = [_]type{ c_int, c_long, c_longlong };
+ const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong };
+ const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong };
+
+ const list: []const type = if (@typeInfo(SuffixType).Int.signedness == .unsigned)
+ &unsigned
+ else if (radix == .decimal)
+ &signed_decimal
+ else
+ &signed_oct_hex;
+
+ var pos = mem.indexOfScalar(type, list, SuffixType).?;
+
+ while (pos < list.len) : (pos += 1) {
+ if (number >= math.minInt(list[pos]) and number <= math.maxInt(list[pos])) {
+ return list[pos];
+ }
+ }
+ @compileError("Integer literal is too large");
+}
+
+/// Promote the type of an integer literal until it fits as C would.
+/// This is for translate-c and is not intended for general use.
+pub fn promoteIntLiteral(
+ comptime SuffixType: type,
+ comptime number: comptime_int,
+ comptime radix: CIntLiteralRadix,
+) PromoteIntLiteralReturnType(SuffixType, number, radix) {
+ return number;
+}
+
+test "promoteIntLiteral" {
+ const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hexadecimal);
+ testing.expectEqual(c_uint, @TypeOf(signed_hex));
+
+ if (math.maxInt(c_longlong) == math.maxInt(c_int)) return;
+
+ const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal);
+ const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hexadecimal);
+
+ if (math.maxInt(c_long) > math.maxInt(c_int)) {
+ testing.expectEqual(c_long, @TypeOf(signed_decimal));
+ testing.expectEqual(c_ulong, @TypeOf(unsigned));
+ } else {
+ testing.expectEqual(c_longlong, @TypeOf(signed_decimal));
+ testing.expectEqual(c_ulonglong, @TypeOf(unsigned));
+ }
+}
+
/// For a given function type, returns a tuple type which fields will
/// correspond to the argument types.
///
diff --git a/src/translate_c.zig b/src/translate_c.zig
@@ -4435,40 +4435,68 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
switch (m.list[m.i].id) {
.IntegerLiteral => |suffix| {
+ var radix: []const u8 = "decimal";
if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
switch (lit_bytes[1]) {
'0'...'7' => {
// Octal
- lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes});
+ lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes[1..]});
+ radix = "octal";
},
'X' => {
// Hexadecimal with capital X, valid in C but not in Zig
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});
+ radix = "hexadecimal";
+ },
+ 'x' => {
+ radix = "hexadecimal";
},
else => {},
}
}
- if (suffix == .none) {
- return transCreateNodeNumber(c, lit_bytes, .int);
- }
-
const type_node = try Tag.type.create(c.arena, switch (suffix) {
+ .none => "c_int",
.u => "c_uint",
.l => "c_long",
.lu => "c_ulong",
.ll => "c_longlong",
.llu => "c_ulonglong",
- else => unreachable,
+ .f => unreachable,
});
lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
- .u, .l => @as(u8, 1),
+ .none => @as(u8, 0),
+ .u, .l => 1,
.lu, .ll => 2,
.llu => 3,
- else => unreachable,
+ .f => unreachable,
}];
- const rhs = try transCreateNodeNumber(c, lit_bytes, .int);
- return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
+
+ const value = std.fmt.parseInt(i128, lit_bytes, 0) catch math.maxInt(i128);
+
+ // make the output less noisy by skipping promoteIntLiteral where
+ // it's guaranteed to not be required because of C standard type constraints
+ const guaranteed_to_fit = switch (suffix) {
+ .none => if (math.cast(i16, value)) |_| true else |_| false,
+ .u => if (math.cast(u16, value)) |_| true else |_| false,
+ .l => if (math.cast(i32, value)) |_| true else |_| false,
+ .lu => if (math.cast(u32, value)) |_| true else |_| false,
+ .ll => if (math.cast(i64, value)) |_| true else |_| false,
+ .llu => if (math.cast(u64, value)) |_| true else |_| false,
+ .f => unreachable,
+ };
+
+ const literal_node = try transCreateNodeNumber(c, lit_bytes, .int);
+
+ if (guaranteed_to_fit) {
+ return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = literal_node });
+ } else {
+ return Tag.std_meta_promoteIntLiteral.create(c.arena, .{
+ .type = type_node,
+ .value = literal_node,
+ .radix = try Tag.enum_literal.create(c.arena, radix),
+ });
+ }
},
.FloatLiteral => |suffix| {
if (lit_bytes[0] == '.')
diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig
@@ -39,6 +39,7 @@ pub const Node = extern union {
float_literal,
string_literal,
char_literal,
+ enum_literal,
identifier,
@"if",
/// if (!operand) break;
@@ -117,6 +118,7 @@ pub const Node = extern union {
/// @intCast(lhs, rhs)
int_cast,
/// @rem(lhs, rhs)
+ std_meta_promoteIntLiteral,
rem,
/// @divTrunc(lhs, rhs)
div_trunc,
@@ -312,6 +314,7 @@ pub const Node = extern union {
.float_literal,
.string_literal,
.char_literal,
+ .enum_literal,
.identifier,
.warning,
.type,
@@ -328,6 +331,7 @@ pub const Node = extern union {
.tuple => Payload.TupleInit,
.container_init => Payload.ContainerInit,
.std_meta_cast => Payload.Infix,
+ .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
.block => Payload.Block,
.c_pointer, .single_pointer => Payload.Pointer,
.array_type => Payload.Array,
@@ -651,6 +655,15 @@ pub const Payload = struct {
field_name: []const u8,
},
};
+
+ pub const PromoteIntLiteral = struct {
+ base: Payload,
+ data: struct {
+ value: Node,
+ type: Node,
+ radix: Node,
+ },
+ };
};
/// Converts the nodes into a Zig ast.
@@ -821,6 +834,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
const import_node = try renderStdImport(c, "meta", "cast");
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
},
+ .std_meta_promoteIntLiteral => {
+ const payload = node.castTag(.std_meta_promoteIntLiteral).?.data;
+ const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
+ return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
+ },
.std_meta_sizeof => {
const payload = node.castTag(.std_meta_sizeof).?.data;
const import_node = try renderStdImport(c, "meta", "sizeof");
@@ -988,6 +1006,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
.data = undefined,
});
},
+ .enum_literal => {
+ const payload = node.castTag(.enum_literal).?.data;
+ _ = try c.addToken(.period, ".");
+ return c.addNode(.{
+ .tag = .enum_literal,
+ .main_token = try c.addToken(.identifier, payload),
+ .data = undefined,
+ });
+ },
.fail_decl => {
const payload = node.castTag(.fail_decl).?.data;
// pub const name = @compileError(msg);
@@ -1982,11 +2009,13 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.typeof,
.std_meta_sizeof,
.std_meta_cast,
+ .std_meta_promoteIntLiteral,
.std_mem_zeroinit,
.integer_literal,
.float_literal,
.string_literal,
.char_literal,
+ .enum_literal,
.identifier,
.field_access,
.ptr_cast,
diff --git a/test/translate_c.zig b/test/translate_c.zig
@@ -232,12 +232,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ | (*((unsigned char *)(p) + 1) << 8) \
\\ | (*((unsigned char *)(p) + 2) << 16))
, &[_][]const u8{
- \\pub const FOO = (foo + 2).*;
+ \\pub const FOO = (foo + @as(c_int, 2)).*;
,
- \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9);
+ \\pub const VALUE = ((((@as(c_int, 1) + (@as(c_int, 2) * @as(c_int, 3))) + (@as(c_int, 4) * @as(c_int, 5))) + @as(c_int, 6)) << @as(c_int, 7)) | @boolToInt(@as(c_int, 8) == @as(c_int, 9));
,
- \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16)) {
- \\ return (@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16);
+ \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 1)).* << @as(c_int, 8))) | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 2)).* << @as(c_int, 16))) {
+ \\ return (@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 1)).* << @as(c_int, 8))) | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 2)).* << @as(c_int, 16));
\\}
});
@@ -312,14 +312,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return type_1;
\\}
,
- \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 });
+ \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ @as(c_int, 200), @as(c_int, 200), @as(c_int, 200), @as(c_int, 255) });
,
\\pub const struct_boom_t = extern struct {
\\ i1: c_int,
\\};
\\pub const boom_t = struct_boom_t;
,
- \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{1});
+ \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{@as(c_int, 1)});
});
cases.add("complex switch",
@@ -343,8 +343,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("correct semicolon after infixop",
\\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
, &[_][]const u8{
- \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != 0) {
- \\ return (_fp.*._flags & _IO_ERR_SEEN) != 0;
+ \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0)) {
+ \\ return (_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0);
\\}
});
@@ -352,11 +352,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define FOO(x) ((x >= 0) + (x >= 0))
\\#define BAR 1 && 2 > 4
, &[_][]const u8{
- \\pub fn FOO(x: anytype) callconv(.Inline) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) {
- \\ return @boolToInt(x >= 0) + @boolToInt(x >= 0);
+ \\pub fn FOO(x: anytype) callconv(.Inline) @TypeOf(@boolToInt(x >= @as(c_int, 0)) + @boolToInt(x >= @as(c_int, 0))) {
+ \\ return @boolToInt(x >= @as(c_int, 0)) + @boolToInt(x >= @as(c_int, 0));
\\}
,
- \\pub const BAR = (1 != 0) and (2 > 4);
+ \\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4));
});
cases.add("struct with aligned fields",
@@ -401,15 +401,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ break :blk bar;
\\};
,
- \\pub fn bar(x: anytype) callconv(.Inline) @TypeOf(baz(1, 2)) {
+ \\pub fn bar(x: anytype) callconv(.Inline) @TypeOf(baz(@as(c_int, 1), @as(c_int, 2))) {
\\ return blk: {
\\ _ = &x;
- \\ _ = 3;
- \\ _ = 4 == 4;
- \\ _ = 5 * 6;
- \\ _ = baz(1, 2);
- \\ _ = 2 % 2;
- \\ break :blk baz(1, 2);
+ \\ _ = @as(c_int, 3);
+ \\ _ = @as(c_int, 4) == @as(c_int, 4);
+ \\ _ = @as(c_int, 5) * @as(c_int, 6);
+ \\ _ = baz(@as(c_int, 1), @as(c_int, 2));
+ \\ _ = @as(c_int, 2) % @as(c_int, 2);
+ \\ break :blk baz(@as(c_int, 1), @as(c_int, 2));
\\ };
\\}
});
@@ -418,9 +418,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define foo 1
\\#define inline 2
, &[_][]const u8{
- \\pub const foo = 1;
+ \\pub const foo = @as(c_int, 1);
,
- \\pub const @"inline" = 2;
+ \\pub const @"inline" = @as(c_int, 2);
});
cases.add("macro line continuation",
@@ -507,7 +507,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("#define hex literal with capital X",
\\#define VAL 0XF00D
, &[_][]const u8{
- \\pub const VAL = 0xF00D;
+ \\pub const VAL = @import("std").meta.promoteIntLiteral(c_int, 0xF00D, .hexadecimal);
});
cases.add("anonymous struct & unions",
@@ -878,7 +878,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("macro with left shift",
\\#define REDISMODULE_READ (1<<0)
, &[_][]const u8{
- \\pub const REDISMODULE_READ = 1 << 0;
+ \\pub const REDISMODULE_READ = @as(c_int, 1) << @as(c_int, 0);
});
cases.add("macro with right shift",
@@ -887,7 +887,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub const FLASH_SIZE = @as(c_ulong, 0x200000);
,
- \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> 1;
+ \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1);
});
cases.add("double define struct",
@@ -955,14 +955,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("#define an unsigned integer literal",
\\#define CHANNEL_COUNT 24
, &[_][]const u8{
- \\pub const CHANNEL_COUNT = 24;
+ \\pub const CHANNEL_COUNT = @as(c_int, 24);
});
cases.add("#define referencing another #define",
\\#define THING2 THING1
\\#define THING1 1234
, &[_][]const u8{
- \\pub const THING1 = 1234;
+ \\pub const THING1 = @as(c_int, 1234);
,
\\pub const THING2 = THING1;
});
@@ -1008,7 +1008,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("macro with parens around negative number",
\\#define LUA_GLOBALSINDEX (-10002)
, &[_][]const u8{
- \\pub const LUA_GLOBALSINDEX = -10002;
+ \\pub const LUA_GLOBALSINDEX = -@as(c_int, 10002);
});
cases.add(
@@ -1091,8 +1091,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define foo 1 //foo
\\#define bar /* bar */ 2
, &[_][]const u8{
- "pub const foo = 1;",
- "pub const bar = 2;",
+ "pub const foo = @as(c_int, 1);",
+ "pub const bar = @as(c_int, 2);",
});
cases.add("string prefix",
@@ -1722,7 +1722,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("comment after integer literal",
\\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
- \\pub const SDL_INIT_VIDEO = 0x00000020;
+ \\pub const SDL_INIT_VIDEO = @as(c_int, 0x00000020);
});
cases.add("u integer suffix after hex literal",
@@ -1836,8 +1836,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub extern var c: c_int;
,
- \\pub fn BASIC(c_1: anytype) callconv(.Inline) @TypeOf(c_1 * 2) {
- \\ return c_1 * 2;
+ \\pub fn BASIC(c_1: anytype) callconv(.Inline) @TypeOf(c_1 * @as(c_int, 2)) {
+ \\ return c_1 * @as(c_int, 2);
\\}
,
\\pub fn FOO(L: anytype, b: anytype) callconv(.Inline) @TypeOf(L + b) {
@@ -2481,7 +2481,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return array[@intCast(c_uint, index)];
\\}
,
- \\pub const ACCESS = array[2];
+ \\pub const ACCESS = array[@as(c_int, 2)];
});
cases.add("cast signed array index to unsigned",
@@ -3097,7 +3097,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const BAR = @import("std").meta.cast(?*c_void, a);
,
- \\pub const BAZ = @import("std").meta.cast(u32, 2);
+ \\pub const BAZ = @import("std").meta.cast(u32, @as(c_int, 2));
});
cases.add("macro with cast to unsigned short, long, and long long",
@@ -3105,9 +3105,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define CURLAUTH_BASIC ((unsigned long) 1)
\\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
, &[_][]const u8{
- \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, 1);
- \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, 1);
- \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, 1);
+ \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, @as(c_int, 1));
+ \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, @as(c_int, 1));
+ \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, @as(c_int, 1));
});
cases.add("macro conditional operator",
@@ -3202,7 +3202,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ bar_1 = 2;
\\}
,
- \\pub const bar = 4;
+ \\pub const bar = @as(c_int, 4);
});
cases.add("don't export inline functions",
@@ -3331,9 +3331,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define NULL ((void*)0)
\\#define FOO ((int)0x8000)
, &[_][]const u8{
- \\pub const NULL = @import("std").meta.cast(?*c_void, 0);
+ \\pub const NULL = @import("std").meta.cast(?*c_void, @as(c_int, 0));
,
- \\pub const FOO = @import("std").meta.cast(c_int, 0x8000);
+ \\pub const FOO = @import("std").meta.cast(c_int, @import("std").meta.promoteIntLiteral(c_int, 0x8000, .hexadecimal));
});
if (std.Target.current.abi == .msvc) {
@@ -3398,4 +3398,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ unnamed_0: struct_unnamed_2,
\\};
});
+
+ cases.add("integer literal promotion",
+ \\#define GUARANTEED_TO_FIT_1 1024
+ \\#define GUARANTEED_TO_FIT_2 10241024L
+ \\#define GUARANTEED_TO_FIT_3 20482048LU
+ \\#define MAY_NEED_PROMOTION_1 10241024
+ \\#define MAY_NEED_PROMOTION_2 307230723072L
+ \\#define MAY_NEED_PROMOTION_3 819281928192LU
+ \\#define MAY_NEED_PROMOTION_HEX 0x80000000
+ \\#define MAY_NEED_PROMOTION_OCT 020000000000
+ , &[_][]const u8{
+ \\pub const GUARANTEED_TO_FIT_1 = @as(c_int, 1024);
+ \\pub const GUARANTEED_TO_FIT_2 = @as(c_long, 10241024);
+ \\pub const GUARANTEED_TO_FIT_3 = @as(c_ulong, 20482048);
+ \\pub const MAY_NEED_PROMOTION_1 = @import("std").meta.promoteIntLiteral(c_int, 10241024, .decimal);
+ \\pub const MAY_NEED_PROMOTION_2 = @import("std").meta.promoteIntLiteral(c_long, 307230723072, .decimal);
+ \\pub const MAY_NEED_PROMOTION_3 = @import("std").meta.promoteIntLiteral(c_ulong, 819281928192, .decimal);
+ \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);
+ \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);
+ });
}