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" {
10941094 testing.expect(sizeof(c_void) == 1);
10951095}
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
10971127/// For a given function type, returns a tuple type which fields will
10981128/// correspond to the argument types.
10991129///
src/translate_c.zig+37-9
......@@ -4431,40 +4431,68 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
44314431
44324432 switch (m.list[m.i].id) {
44334433 .IntegerLiteral => |suffix| {
4434 var radix: []const u8 = "decimal";
44344435 if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
44354436 switch (lit_bytes[1]) {
44364437 '0'...'7' => {
44374438 // Octal
44384439 lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes});
4440 radix = "octal";
44394441 },
44404442 'X' => {
44414443 // Hexadecimal with capital X, valid in C but not in Zig
44424444 lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});
4445 radix = "hexadecimal";
4446 },
4447 'x' => {
4448 radix = "hexadecimal";
44434449 },
44444450 else => {},
44454451 }
44464452 }
44474453
4448 if (suffix == .none) {
4449 return transCreateNodeNumber(c, lit_bytes, .int);
4450 }
4451
44524454 const type_node = try Tag.type.create(c.arena, switch (suffix) {
4455 .none => "c_int",
44534456 .u => "c_uint",
44544457 .l => "c_long",
44554458 .lu => "c_ulong",
44564459 .ll => "c_longlong",
44574460 .llu => "c_ulonglong",
4458 else => unreachable,
4461 .f => unreachable,
44594462 });
44604463 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,
44624466 .lu, .ll => 2,
44634467 .llu => 3,
4464 else => unreachable,
4468 .f => unreachable,
44654469 }];
4466 const rhs = try transCreateNodeNumber(c, lit_bytes, .int);
4467 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
4470
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 }
44684496 },
44694497 .FloatLiteral => |suffix| {
44704498 if (lit_bytes[0] == '.')
src/translate_c/ast.zig+29
......@@ -39,6 +39,7 @@ pub const Node = extern union {
3939 float_literal,
4040 string_literal,
4141 char_literal,
42 enum_literal,
4243 identifier,
4344 @"if",
4445 /// if (!operand) break;
......@@ -117,6 +118,7 @@ pub const Node = extern union {
117118 /// @intCast(lhs, rhs)
118119 int_cast,
119120 /// @rem(lhs, rhs)
121 std_meta_promoteIntLiteral,
120122 rem,
121123 /// @divTrunc(lhs, rhs)
122124 div_trunc,
......@@ -312,6 +314,7 @@ pub const Node = extern union {
312314 .float_literal,
313315 .string_literal,
314316 .char_literal,
317 .enum_literal,
315318 .identifier,
316319 .warning,
317320 .type,
......@@ -328,6 +331,7 @@ pub const Node = extern union {
328331 .tuple => Payload.TupleInit,
329332 .container_init => Payload.ContainerInit,
330333 .std_meta_cast => Payload.Infix,
334 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
331335 .block => Payload.Block,
332336 .c_pointer, .single_pointer => Payload.Pointer,
333337 .array_type => Payload.Array,
......@@ -651,6 +655,15 @@ pub const Payload = struct {
651655 field_name: []const u8,
652656 },
653657 };
658
659 pub const PromoteIntLiteral = struct {
660 base: Payload,
661 data: struct {
662 value: Node,
663 type: Node,
664 radix: Node,
665 },
666 };
654667};
655668
656669/// Converts the nodes into a Zig ast.
......@@ -821,6 +834,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
821834 const import_node = try renderStdImport(c, "meta", "cast");
822835 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
823836 },
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 },
824842 .std_meta_sizeof => {
825843 const payload = node.castTag(.std_meta_sizeof).?.data;
826844 const import_node = try renderStdImport(c, "meta", "sizeof");
......@@ -988,6 +1006,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
9881006 .data = undefined,
9891007 });
9901008 },
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 },
9911018 .fail_decl => {
9921019 const payload = node.castTag(.fail_decl).?.data;
9931020 // pub const name = @compileError(msg);
......@@ -1982,11 +2009,13 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
19822009 .typeof,
19832010 .std_meta_sizeof,
19842011 .std_meta_cast,
2012 .std_meta_promoteIntLiteral,
19852013 .std_mem_zeroinit,
19862014 .integer_literal,
19872015 .float_literal,
19882016 .string_literal,
19892017 .char_literal,
2018 .enum_literal,
19902019 .identifier,
19912020 .field_access,
19922021 .ptr_cast,