Commit 679910ecec

xackus <14938807+xackus@users.noreply.github.com>
2021-03-02 17:40:34
translate-c: promote int literals to bigger types
1 parent 9cd038d
Changed files (3)
lib
src
lib/std/meta.zig
@@ -1094,6 +1094,36 @@ test "sizeof" {
     testing.expect(sizeof(c_void) == 1);
 }
 
+pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal };
+
+fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime target: 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 (target >= math.minInt(list[pos]) and target <= math.maxInt(list[pos])) {
+            return list[pos];
+        }
+    }
+    @compileError("Integer literal does not fit in compatible types");
+}
+
+/// 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 target: comptime_int, comptime radix: CIntLiteralRadix) PromoteIntLiteralReturnType(SuffixType, target, radix) {
+    return @as(PromoteIntLiteralReturnType(SuffixType, target, radix), target);
+}
+
 /// For a given function type, returns a tuple type which fields will
 /// correspond to the argument types.
 ///
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,
src/translate_c.zig
@@ -4431,40 +4431,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});
+                        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] == '.')