Merge pull request #5662 from shtanton/meta-cast

Adds std.meta.cast and uses it to simplify translate-c
This commit is contained in:
Andrew Kelley
2020-06-22 19:54:30 -04:00
committed by GitHub
3 changed files with 102 additions and 158 deletions

View File

@@ -693,3 +693,85 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
},
});
}
/// Given a type and value, cast the value to the type as c would.
/// This is for translate-c and is not intended for general use.
pub fn cast(comptime DestType: type, target: var) DestType {
const TargetType = @TypeOf(target);
switch (@typeInfo(DestType)) {
.Pointer => {
switch (@typeInfo(TargetType)) {
.Int, .ComptimeInt => {
return @intToPtr(DestType, target);
},
.Pointer => |ptr| {
return @ptrCast(DestType, @alignCast(ptr.alignment, target));
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));
}
},
else => {},
}
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
switch (@typeInfo(TargetType)) {
.Int, .ComptimeInt => {
return @intToPtr(DestType, target);
},
.Pointer => |ptr| {
return @ptrCast(DestType, @alignCast(ptr.alignment, target));
},
.Optional => |target_opt| {
if (@typeInfo(target_opt.child) == .Pointer) {
return @ptrCast(DestType, @alignCast(@alignOf(target_opt.child.Child), target));
}
},
else => {},
}
}
},
.Enum, .EnumLiteral => {
if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
return @intToEnum(DestType, target);
}
},
.Int, .ComptimeInt => {
switch (@typeInfo(TargetType)) {
.Pointer => {
return @as(DestType, @ptrToInt(target));
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
return @as(DestType, @ptrToInt(target));
}
},
.Enum, .EnumLiteral => {
return @as(DestType, @enumToInt(target));
},
else => {},
}
},
else => {},
}
return @as(DestType, target);
}
test "std.meta.cast" {
const E = enum(u2) {
Zero,
One,
Two,
};
var i = @as(i64, 10);
testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));
testing.expect(cast(u64, @as(u32, 10)) == @as(u64, 10));
testing.expect(cast(E, 1) == .One);
testing.expect(cast(u8, E.Two) == 2);
testing.expect(cast(*u64, &i).* == @as(u64, 10));
}

View File

@@ -5668,161 +5668,23 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
const lparen = try appendToken(c, .LParen, "(");
if (saw_integer_literal) {
//( if (@typeInfo(dest) == .Pointer))
// @intToPtr(dest, x)
//else
// @as(dest, x) )
const if_node = try transCreateNodeIf(c);
const type_info_node = try c.createBuiltinCall("@typeInfo", 1);
type_info_node.params()[0] = inner_node;
type_info_node.rparen_token = try appendToken(c, .LParen, ")");
const cmp_node = try c.arena.create(ast.Node.InfixOp);
cmp_node.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_node.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
};
if_node.condition = &cmp_node.base;
_ = try appendToken(c, .RParen, ")");
//(@import("std").meta.cast(dest, x))
const import_fn_call = try c.createBuiltinCall("@import", 1);
const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
import_fn_call.params()[0] = std_node;
import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
int_to_ptr.params()[0] = inner_node;
int_to_ptr.params()[1] = node_to_cast;
int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
if_node.body = &int_to_ptr.base;
const else_node = try transCreateNodeElse(c);
if_node.@"else" = else_node;
const as_node = try c.createBuiltinCall("@as", 2);
as_node.params()[0] = inner_node;
as_node.params()[1] = node_to_cast;
as_node.rparen_token = try appendToken(c, .RParen, ")");
else_node.body = &as_node.base;
const group_node = try c.arena.create(ast.Node.GroupedExpression);
group_node.* = .{
.lparen = lparen,
.expr = &if_node.base,
.rparen = try appendToken(c, .RParen, ")"),
};
return &group_node.base;
}
//( if (@typeInfo(@TypeOf(x)) == .Pointer)
// @ptrCast(dest, @alignCast(@alignOf(dest.Child), x))
//else if (@typeInfo(@TypeOf(x)) == .Int and @typeInfo(dest) == .Pointer))
// @intToPtr(dest, x)
//else
// @as(dest, x) )
const if_1 = try transCreateNodeIf(c);
const type_info_1 = try c.createBuiltinCall("@typeInfo", 1);
const type_of_1 = try c.createBuiltinCall("@TypeOf", 1);
type_info_1.params()[0] = &type_of_1.base;
type_of_1.params()[0] = node_to_cast;
type_of_1.rparen_token = try appendToken(c, .RParen, ")");
type_info_1.rparen_token = try appendToken(c, .RParen, ")");
const cmp_1 = try c.arena.create(ast.Node.InfixOp);
cmp_1.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_1.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
};
if_1.condition = &cmp_1.base;
_ = try appendToken(c, .RParen, ")");
const period_tok = try appendToken(c, .Period, ".");
const child_ident = try transCreateNodeIdentifier(c, "Child");
const inner_node_child = try c.arena.create(ast.Node.InfixOp);
inner_node_child.* = .{
.op_token = period_tok,
.lhs = inner_node,
.op = .Period,
.rhs = child_ident,
};
const align_of = try c.createBuiltinCall("@alignOf", 1);
align_of.params()[0] = &inner_node_child.base;
align_of.rparen_token = try appendToken(c, .RParen, ")");
// hack to get zig fmt to render a comma in builtin calls
_ = try appendToken(c, .Comma, ",");
const align_cast = try c.createBuiltinCall("@alignCast", 2);
align_cast.params()[0] = &align_of.base;
align_cast.params()[1] = node_to_cast;
align_cast.rparen_token = try appendToken(c, .RParen, ")");
const ptr_cast = try c.createBuiltinCall("@ptrCast", 2);
ptr_cast.params()[0] = inner_node;
ptr_cast.params()[1] = &align_cast.base;
ptr_cast.rparen_token = try appendToken(c, .RParen, ")");
if_1.body = &ptr_cast.base;
const else_1 = try transCreateNodeElse(c);
if_1.@"else" = else_1;
const if_2 = try transCreateNodeIf(c);
const type_info_2 = try c.createBuiltinCall("@typeInfo", 1);
const type_of_2 = try c.createBuiltinCall("@TypeOf", 1);
type_info_2.params()[0] = &type_of_2.base;
type_of_2.params()[0] = node_to_cast;
type_of_2.rparen_token = try appendToken(c, .RParen, ")");
type_info_2.rparen_token = try appendToken(c, .RParen, ")");
const cmp_2 = try c.arena.create(ast.Node.InfixOp);
cmp_2.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_2.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Int"),
};
if_2.condition = &cmp_2.base;
const cmp_4 = try c.arena.create(ast.Node.InfixOp);
cmp_4.* = .{
.op_token = try appendToken(c, .Keyword_and, "and"),
.lhs = &cmp_2.base,
.op = .BoolAnd,
.rhs = undefined,
};
const type_info_3 = try c.createBuiltinCall("@typeInfo", 1);
type_info_3.params()[0] = inner_node;
type_info_3.rparen_token = try appendToken(c, .LParen, ")");
const cmp_3 = try c.arena.create(ast.Node.InfixOp);
cmp_3.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_3.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
};
cmp_4.rhs = &cmp_3.base;
if_2.condition = &cmp_4.base;
else_1.body = &if_2.base;
_ = try appendToken(c, .RParen, ")");
const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
int_to_ptr.params()[0] = inner_node;
int_to_ptr.params()[1] = node_to_cast;
int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
if_2.body = &int_to_ptr.base;
const else_2 = try transCreateNodeElse(c);
if_2.@"else" = else_2;
const as = try c.createBuiltinCall("@as", 2);
as.params()[0] = inner_node;
as.params()[1] = node_to_cast;
as.rparen_token = try appendToken(c, .RParen, ")");
else_2.body = &as.base;
const cast_fn_call = try c.createCall(outer_field_access, 2);
cast_fn_call.params()[0] = inner_node;
cast_fn_call.params()[1] = node_to_cast;
cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
const group_node = try c.arena.create(ast.Node.GroupedExpression);
group_node.* = .{
.lparen = lparen,
.expr = &if_1.base,
.expr = &cast_fn_call.base,
.rparen = try appendToken(c, .RParen, ")"),
};
return &group_node.base;

View File

@@ -1473,7 +1473,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("macro pointer cast",
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
, &[_][]const u8{
\\pub const NRF_GPIO = (if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, @alignCast(@alignOf([*c]NRF_GPIO_Type.Child), NRF_GPIO_BASE)) else if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Int and @typeInfo([*c]NRF_GPIO_Type) == .Pointer) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
\\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
});
cases.add("basic macro function",
@@ -2683,11 +2683,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define FOO(bar) baz((void *)(baz))
\\#define BAR (void*) a
, &[_][]const u8{
\\pub inline fn FOO(bar: var) @TypeOf(baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)))) {
\\ return baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)));
\\pub inline fn FOO(bar: var) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
\\ return baz((@import("std").meta.cast(?*c_void, baz)));
\\}
,
\\pub const BAR = (if (@typeInfo(@TypeOf(a)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), a)) else if (@typeInfo(@TypeOf(a)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, a) else @as(?*c_void, a));
\\pub const BAR = (@import("std").meta.cast(?*c_void, a));
});
cases.add("macro conditional operator",
@@ -2905,8 +2905,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
\\
, &[_][]const u8{
\\pub inline fn DefaultScreen(dpy: var) @TypeOf((if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen) {
\\ return (if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen;
\\pub inline fn DefaultScreen(dpy: var) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) {
\\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;
\\}
});
@@ -2914,9 +2914,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define NULL ((void*)0)
\\#define FOO ((int)0x8000)
, &[_][]const u8{
\\pub const NULL = (if (@typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, 0) else @as(?*c_void, 0));
\\pub const NULL = (@import("std").meta.cast(?*c_void, 0));
,
\\pub const FOO = (if (@typeInfo(c_int) == .Pointer) @intToPtr(c_int, 0x8000) else @as(c_int, 0x8000));
\\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));
});
if (std.Target.current.abi == .msvc) {