translate-c: promote int literals to bigger types

This commit is contained in:
xackus 2021-03-02 17:40:34 +01:00
parent 9cd038d73a
commit 679910ecec
3 changed files with 96 additions and 9 deletions

View File

@ -1094,6 +1094,36 @@ test "sizeof" {
testing.expect(sizeof(c_void) == 1); 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 /// For a given function type, returns a tuple type which fields will
/// correspond to the argument types. /// correspond to the argument types.
/// ///

View File

@ -4431,40 +4431,68 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
switch (m.list[m.i].id) { switch (m.list[m.i].id) {
.IntegerLiteral => |suffix| { .IntegerLiteral => |suffix| {
var radix: []const u8 = "decimal";
if (lit_bytes.len > 2 and lit_bytes[0] == '0') { if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
switch (lit_bytes[1]) { switch (lit_bytes[1]) {
'0'...'7' => { '0'...'7' => {
// Octal // 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});
radix = "octal";
}, },
'X' => { 'X' => {
// Hexadecimal with capital X, valid in C but not in Zig // Hexadecimal with capital X, valid in C but not in Zig
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]}); lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]});
radix = "hexadecimal";
},
'x' => {
radix = "hexadecimal";
}, },
else => {}, else => {},
} }
} }
if (suffix == .none) {
return transCreateNodeNumber(c, lit_bytes, .int);
}
const type_node = try Tag.type.create(c.arena, switch (suffix) { const type_node = try Tag.type.create(c.arena, switch (suffix) {
.none => "c_int",
.u => "c_uint", .u => "c_uint",
.l => "c_long", .l => "c_long",
.lu => "c_ulong", .lu => "c_ulong",
.ll => "c_longlong", .ll => "c_longlong",
.llu => "c_ulonglong", .llu => "c_ulonglong",
else => unreachable, .f => unreachable,
}); });
lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) { 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, .lu, .ll => 2,
.llu => 3, .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| { .FloatLiteral => |suffix| {
if (lit_bytes[0] == '.') if (lit_bytes[0] == '.')

View File

@ -39,6 +39,7 @@ pub const Node = extern union {
float_literal, float_literal,
string_literal, string_literal,
char_literal, char_literal,
enum_literal,
identifier, identifier,
@"if", @"if",
/// if (!operand) break; /// if (!operand) break;
@ -117,6 +118,7 @@ pub const Node = extern union {
/// @intCast(lhs, rhs) /// @intCast(lhs, rhs)
int_cast, int_cast,
/// @rem(lhs, rhs) /// @rem(lhs, rhs)
std_meta_promoteIntLiteral,
rem, rem,
/// @divTrunc(lhs, rhs) /// @divTrunc(lhs, rhs)
div_trunc, div_trunc,
@ -312,6 +314,7 @@ pub const Node = extern union {
.float_literal, .float_literal,
.string_literal, .string_literal,
.char_literal, .char_literal,
.enum_literal,
.identifier, .identifier,
.warning, .warning,
.type, .type,
@ -328,6 +331,7 @@ pub const Node = extern union {
.tuple => Payload.TupleInit, .tuple => Payload.TupleInit,
.container_init => Payload.ContainerInit, .container_init => Payload.ContainerInit,
.std_meta_cast => Payload.Infix, .std_meta_cast => Payload.Infix,
.std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
.block => Payload.Block, .block => Payload.Block,
.c_pointer, .single_pointer => Payload.Pointer, .c_pointer, .single_pointer => Payload.Pointer,
.array_type => Payload.Array, .array_type => Payload.Array,
@ -651,6 +655,15 @@ pub const Payload = struct {
field_name: []const u8, 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. /// 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"); const import_node = try renderStdImport(c, "meta", "cast");
return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); 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 => { .std_meta_sizeof => {
const payload = node.castTag(.std_meta_sizeof).?.data; const payload = node.castTag(.std_meta_sizeof).?.data;
const import_node = try renderStdImport(c, "meta", "sizeof"); const import_node = try renderStdImport(c, "meta", "sizeof");
@ -988,6 +1006,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
.data = undefined, .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 => { .fail_decl => {
const payload = node.castTag(.fail_decl).?.data; const payload = node.castTag(.fail_decl).?.data;
// pub const name = @compileError(msg); // pub const name = @compileError(msg);
@ -1982,11 +2009,13 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.typeof, .typeof,
.std_meta_sizeof, .std_meta_sizeof,
.std_meta_cast, .std_meta_cast,
.std_meta_promoteIntLiteral,
.std_mem_zeroinit, .std_mem_zeroinit,
.integer_literal, .integer_literal,
.float_literal, .float_literal,
.string_literal, .string_literal,
.char_literal, .char_literal,
.enum_literal,
.identifier, .identifier,
.field_access, .field_access,
.ptr_cast, .ptr_cast,