diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7ec29dcd0e..7fe0df3dea 100644 --- a/lib/std/meta.zig +++ b/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. /// diff --git a/src/translate_c.zig b/src/translate_c.zig index 2770ffb4cb..34655d3bb5 100644 --- a/src/translate_c.zig +++ b/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] == '.') diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index e984274c75..e5f76cc1de 100644 --- 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,