authorgravatar for 14938807+xackus@users.noreply.github.comxackus <14938807+xackus@users.noreply.github.com> 2021-03-02 17:40:34+01:00
committergravatar for 14938807+xackus@users.noreply.github.comxackus <14938807+xackus@users.noreply.github.com> 2021-03-05 21:04:27+01:00
log679910ecec5cb8d77cbb599ce5df9459615e2d50
tree4a47a31015f91168cbaee4b0faa2895c685f0c09
parent9cd038d73a174706ec0a51ab9db0c04b095e019d

translate-c: promote int literals to bigger types


3 files changed, 96 insertions(+), 9 deletions(-)

lib/std/meta.zig+30
...@@ -1094,6 +1094,36 @@ test "sizeof" {...@@ -1094,6 +1094,36 @@ test "sizeof" {
1094 testing.expect(sizeof(c_void) == 1);1094 testing.expect(sizeof(c_void) == 1);
1095}1095}
10961096
1097pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal };
1098
1099fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) type {
1100 const signed_decimal = [_]type{ c_int, c_long, c_longlong };
1101 const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong };
1102 const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong };
1103
1104 const list: []const type = if (@typeInfo(SuffixType).Int.signedness == .unsigned)
1105 &unsigned
1106 else if (radix == .decimal)
1107 &signed_decimal
1108 else
1109 &signed_oct_hex;
1110
1111 var pos = mem.indexOfScalar(type, list, SuffixType).?;
1112
1113 while (pos < list.len) : (pos += 1) {
1114 if (target >= math.minInt(list[pos]) and target <= math.maxInt(list[pos])) {
1115 return list[pos];
1116 }
1117 }
1118 @compileError("Integer literal does not fit in compatible types");
1119}
1120
1121/// Promote the type of an integer literal until it fits as C would.
1122/// This is for translate-c and is not intended for general use.
1123pub fn promoteIntLiteral(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) PromoteIntLiteralReturnType(SuffixType, target, radix) {
1124 return @as(PromoteIntLiteralReturnType(SuffixType, target, radix), target);
1125}
1126
1097/// For a given function type, returns a tuple type which fields will1127/// For a given function type, returns a tuple type which fields will
1098/// correspond to the argument types.1128/// correspond to the argument types.
1099///1129///
src/translate_c.zig+37-9
...@@ -4431,40 +4431,68 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {...@@ -4431,40 +4431,68 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
44314431
4432 switch (m.list[m.i].id) {4432 switch (m.list[m.i].id) {
4433 .IntegerLiteral => |suffix| {4433 .IntegerLiteral => |suffix| {
4434 var radix: []const u8 = "decimal";
4434 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {4435 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
4435 switch (lit_bytes[1]) {4436 switch (lit_bytes[1]) {
4436 '0'...'7' => {4437 '0'...'7' => {
4437 // Octal4438 // Octal
4438 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes});4439 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes});
4440 radix = "octal";
4439 },4441 },
4440 'X' => {4442 'X' => {
4441 // Hexadecimal with capital X, valid in C but not in Zig4443 // Hexadecimal with capital X, valid in C but not in Zig
4442 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});4444 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});
4445 radix = "hexadecimal";
4446 },
4447 'x' => {
4448 radix = "hexadecimal";
4443 },4449 },
4444 else => {},4450 else => {},
4445 }4451 }
4446 }4452 }
44474453
4448 if (suffix == .none) {
4449 return transCreateNodeNumber(c, lit_bytes, .int);
4450 }
4451
4452 const type_node = try Tag.type.create(c.arena, switch (suffix) {4454 const type_node = try Tag.type.create(c.arena, switch (suffix) {
4455 .none => "c_int",
4453 .u => "c_uint",4456 .u => "c_uint",
4454 .l => "c_long",4457 .l => "c_long",
4455 .lu => "c_ulong",4458 .lu => "c_ulong",
4456 .ll => "c_longlong",4459 .ll => "c_longlong",
4457 .llu => "c_ulonglong",4460 .llu => "c_ulonglong",
4458 else => unreachable,4461 .f => unreachable,
4459 });4462 });
4460 lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {4463 lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
4461 .u, .l => @as(u8, 1),4464 .none => @as(u8, 0),
4465 .u, .l => 1,
4462 .lu, .ll => 2,4466 .lu, .ll => 2,
4463 .llu => 3,4467 .llu => 3,
4464 else => unreachable,4468 .f => unreachable,
4465 }];4469 }];
4466 const rhs = try transCreateNodeNumber(c, lit_bytes, .int);4470
4467 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });4471 const value = std.fmt.parseInt(i128, lit_bytes, 0) catch math.maxInt(i128);
4472
4473 // make the output less noisy by skipping promoteIntLiteral where
4474 // it's guaranteed to not be required because of C standard type constraints
4475 const guaranteed_to_fit = switch (suffix) {
4476 .none => if (math.cast(i16, value)) |_| true else |_| false,
4477 .u => if (math.cast(u16, value)) |_| true else |_| false,
4478 .l => if (math.cast(i32, value)) |_| true else |_| false,
4479 .lu => if (math.cast(u32, value)) |_| true else |_| false,
4480 .ll => if (math.cast(i64, value)) |_| true else |_| false,
4481 .llu => if (math.cast(u64, value)) |_| true else |_| false,
4482 .f => unreachable,
4483 };
4484
4485 const literal_node = try transCreateNodeNumber(c, lit_bytes, .int);
4486
4487 if (guaranteed_to_fit) {
4488 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = literal_node });
4489 } else {
4490 return Tag.std_meta_promoteIntLiteral.create(c.arena, .{
4491 .type = type_node,
4492 .value = literal_node,
4493 .radix = try Tag.enum_literal.create(c.arena, radix),
4494 });
4495 }
4468 },4496 },
4469 .FloatLiteral => |suffix| {4497 .FloatLiteral => |suffix| {
4470 if (lit_bytes[0] == '.')4498 if (lit_bytes[0] == '.')
src/translate_c/ast.zig+29
...@@ -39,6 +39,7 @@ pub const Node = extern union {...@@ -39,6 +39,7 @@ pub const Node = extern union {
39 float_literal,39 float_literal,
40 string_literal,40 string_literal,
41 char_literal,41 char_literal,
42 enum_literal,
42 identifier,43 identifier,
43 @"if",44 @"if",
44 /// if (!operand) break;45 /// if (!operand) break;
...@@ -117,6 +118,7 @@ pub const Node = extern union {...@@ -117,6 +118,7 @@ pub const Node = extern union {
117 /// @intCast(lhs, rhs)118 /// @intCast(lhs, rhs)
118 int_cast,119 int_cast,
119 /// @rem(lhs, rhs)120 /// @rem(lhs, rhs)
121 std_meta_promoteIntLiteral,
120 rem,122 rem,
121 /// @divTrunc(lhs, rhs)123 /// @divTrunc(lhs, rhs)
122 div_trunc,124 div_trunc,
...@@ -312,6 +314,7 @@ pub const Node = extern union {...@@ -312,6 +314,7 @@ pub const Node = extern union {
312 .float_literal,314 .float_literal,
313 .string_literal,315 .string_literal,
314 .char_literal,316 .char_literal,
317 .enum_literal,
315 .identifier,318 .identifier,
316 .warning,319 .warning,
317 .type,320 .type,
...@@ -328,6 +331,7 @@ pub const Node = extern union {...@@ -328,6 +331,7 @@ pub const Node = extern union {
328 .tuple => Payload.TupleInit,331 .tuple => Payload.TupleInit,
329 .container_init => Payload.ContainerInit,332 .container_init => Payload.ContainerInit,
330 .std_meta_cast => Payload.Infix,333 .std_meta_cast => Payload.Infix,
334 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
331 .block => Payload.Block,335 .block => Payload.Block,
332 .c_pointer, .single_pointer => Payload.Pointer,336 .c_pointer, .single_pointer => Payload.Pointer,
333 .array_type => Payload.Array,337 .array_type => Payload.Array,
...@@ -651,6 +655,15 @@ pub const Payload = struct {...@@ -651,6 +655,15 @@ pub const Payload = struct {
651 field_name: []const u8,655 field_name: []const u8,
652 },656 },
653 };657 };
658
659 pub const PromoteIntLiteral = struct {
660 base: Payload,
661 data: struct {
662 value: Node,
663 type: Node,
664 radix: Node,
665 },
666 };
654};667};
655668
656/// Converts the nodes into a Zig ast.669/// Converts the nodes into a Zig ast.
...@@ -821,6 +834,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -821,6 +834,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
821 const import_node = try renderStdImport(c, "meta", "cast");834 const import_node = try renderStdImport(c, "meta", "cast");
822 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });835 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
823 },836 },
837 .std_meta_promoteIntLiteral => {
838 const payload = node.castTag(.std_meta_promoteIntLiteral).?.data;
839 const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
840 return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
841 },
824 .std_meta_sizeof => {842 .std_meta_sizeof => {
825 const payload = node.castTag(.std_meta_sizeof).?.data;843 const payload = node.castTag(.std_meta_sizeof).?.data;
826 const import_node = try renderStdImport(c, "meta", "sizeof");844 const import_node = try renderStdImport(c, "meta", "sizeof");
...@@ -988,6 +1006,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -988,6 +1006,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
988 .data = undefined,1006 .data = undefined,
989 });1007 });
990 },1008 },
1009 .enum_literal => {
1010 const payload = node.castTag(.enum_literal).?.data;
1011 _ = try c.addToken(.period, ".");
1012 return c.addNode(.{
1013 .tag = .enum_literal,
1014 .main_token = try c.addToken(.identifier, payload),
1015 .data = undefined,
1016 });
1017 },
991 .fail_decl => {1018 .fail_decl => {
992 const payload = node.castTag(.fail_decl).?.data;1019 const payload = node.castTag(.fail_decl).?.data;
993 // pub const name = @compileError(msg);1020 // pub const name = @compileError(msg);
...@@ -1982,11 +2009,13 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -1982,11 +2009,13 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
1982 .typeof,2009 .typeof,
1983 .std_meta_sizeof,2010 .std_meta_sizeof,
1984 .std_meta_cast,2011 .std_meta_cast,
2012 .std_meta_promoteIntLiteral,
1985 .std_mem_zeroinit,2013 .std_mem_zeroinit,
1986 .integer_literal,2014 .integer_literal,
1987 .float_literal,2015 .float_literal,
1988 .string_literal,2016 .string_literal,
1989 .char_literal,2017 .char_literal,
2018 .enum_literal,
1990 .identifier,2019 .identifier,
1991 .field_access,2020 .field_access,
1992 .ptr_cast,2021 .ptr_cast,