Skip to content

Commit

Permalink
Merge pull request #10 from sno2/casts
Browse files Browse the repository at this point in the history
fix cast result types and support more casts
  • Loading branch information
Vexu authored Feb 12, 2025
2 parents 1ed007a + 743c68d commit 8eedcf4
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 91 deletions.
210 changes: 127 additions & 83 deletions src/Translator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1522,7 +1522,7 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)
.paren_expr => |paren_expr| {
return t.transExpr(scope, paren_expr.operand, used);
},
.cast => |cast| return t.transCastExpr(scope, cast, used),
.cast => |cast| return t.transCastExpr(scope, cast, used, .with_as),
.decl_ref_expr => |decl_ref| try t.transDeclRefExpr(scope, decl_ref),
.enumeration_ref => |enum_ref| try t.transDeclRefExpr(scope, enum_ref),
.addr_of_expr => |addr_of_expr| try ZigTag.address_of.create(t.arena, try t.transExpr(scope, addr_of_expr.operand, .used)),
Expand Down Expand Up @@ -1677,25 +1677,7 @@ fn transExprCoercing(t: *Translator, scope: *Scope, expr: Node.Index, used: Resu
.int_literal => return t.transIntLiteral(scope, expr, used, .no_as),
.char_literal => return t.transCharLiteral(scope, expr, used, .no_as),
.float_literal => return t.transFloatLiteral(scope, expr, used, .no_as),
.cast => |cast| {
if (cast.implicit) {
switch (cast.kind) {
.null_to_pointer => return ZigTag.null_literal.init(),
.int_to_float => return t.transExprCoercing(scope, cast.operand, used),
.int_cast => {
if (t.tree.value_map.get(cast.operand)) |val| {
const max_int = try aro.Value.maxInt(cast.qt, t.comp);

if (val.compare(.lte, max_int, t.comp)) {
return t.transExprCoercing(scope, cast.operand, used);
}
}
},
else => {},
}
return t.maybeSuppressResult(used, try t.transCastExpr(scope, cast, used));
}
},
.cast => |cast| return t.transCastExpr(scope, cast, used, .no_as),
.default_init_expr => |default_init| return try t.transDefaultInit(scope, default_init, used, .no_as),
else => {},
}
Expand Down Expand Up @@ -1750,74 +1732,136 @@ fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNod
unreachable; // Unexpected bool expression type
}

fn transCastExpr(t: *Translator, scope: *Scope, cast: Node.Cast, used: ResultUsed) TransError!ZigNode {
switch (cast.kind) {
.lval_to_rval, .no_op, .function_to_pointer => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
return t.maybeSuppressResult(used, sub_expr_node);
},
.int_cast => {
const dest_qt = cast.qt;
const src_qt = cast.operand.qt(t.tree);
fn transCastExpr(
t: *Translator,
scope: *Scope,
cast: Node.Cast,
used: ResultUsed,
suppress_as: SuppressCast,
) TransError!ZigNode {
const to_cast = to_cast: {
switch (cast.kind) {
.lval_to_rval, .no_op, .function_to_pointer => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
return t.maybeSuppressResult(used, sub_expr_node);
},
.int_cast => {
const dest_qt = cast.qt;
const src_qt = cast.operand.qt(t.tree);

const operand_expr = try t.transExprCoercing(scope, cast.operand, used);
const operand_expr = try t.transExprCoercing(scope, cast.operand, used);

const needs_truncate = src_qt.intRankOrder(dest_qt, t.comp).compare(.gt);
const needs_bitcast = src_qt.signedness(t.comp) != dest_qt.signedness(t.comp);
if (needs_truncate and needs_bitcast) {
const as = try ZigTag.as.create(t.arena, .{
.lhs = try t.transTypeIntWidthOf(dest_qt, src_qt.signedness(t.comp) == .signed),
.rhs = try ZigTag.truncate.create(t.arena, operand_expr),
});
return try ZigTag.bit_cast.create(t.arena, as);
} else if (needs_truncate) {
return try ZigTag.truncate.create(t.arena, operand_expr);
} else if (needs_bitcast) {
return try ZigTag.bit_cast.create(t.arena, operand_expr);
}
if (suppress_as == .no_as and cast.implicit) {
if (t.tree.value_map.get(cast.operand)) |val| {
const max_int = try aro.Value.maxInt(cast.qt, t.comp);

return operand_expr;
},
.to_void => {
assert(used == .unused);
return t.transExpr(scope, cast.operand, .unused);
},
.null_to_pointer => {
const as = try ZigTag.as.create(t.arena, .{
.lhs = try t.transType(scope, cast.qt, cast.l_paren),
.rhs = ZigTag.null_literal.init(),
});
return as;
},
.array_to_pointer => {
if (t.tree.value_map.get(cast.operand)) |val| {
const str_qt = cast.qt;
const bytes = t.comp.interner.get(val.ref()).bytes;
var buf = std.ArrayList(u8).init(t.gpa);
defer buf.deinit();
if (val.compare(.lte, max_int, t.comp)) {
return t.transExprCoercing(scope, cast.operand, used);
}
}
}

try buf.ensureUnusedCapacity(bytes.len);
try aro.Value.printString(bytes, str_qt, t.comp, buf.writer());
const needs_truncate = src_qt.intRankOrder(dest_qt, t.comp).compare(.gt);
const needs_bitcast = src_qt.signedness(t.comp) != dest_qt.signedness(t.comp);
if (needs_truncate and needs_bitcast) {
const as = try ZigTag.as.create(t.arena, .{
.lhs = try t.transTypeIntWidthOf(dest_qt, src_qt.signedness(t.comp) == .signed),
.rhs = try ZigTag.truncate.create(t.arena, operand_expr),
});
break :to_cast try ZigTag.bit_cast.create(t.arena, as);
} else if (needs_truncate) {
break :to_cast try ZigTag.truncate.create(t.arena, operand_expr);
} else if (needs_bitcast) {
break :to_cast try ZigTag.bit_cast.create(t.arena, operand_expr);
}
break :to_cast operand_expr;
},
.to_void => {
assert(used == .unused);
return try t.transExpr(scope, cast.operand, .unused);
},
.null_to_pointer => {
break :to_cast ZigTag.null_literal.init();
},
.array_to_pointer => {
if (t.tree.value_map.get(cast.operand)) |val| {
const str_qt = cast.qt;
const bytes = t.comp.interner.get(val.ref()).bytes;
var buf = std.ArrayList(u8).init(t.gpa);
defer buf.deinit();

return try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, buf.items));
}
return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)});
},
.int_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
if (sub_expr_node.isBoolRes()) return sub_expr_node;
return ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
},
.float_cast => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
return ZigTag.float_cast.create(t.arena, sub_expr_node);
},
.float_to_int => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
return ZigTag.int_from_float.create(t.arena, sub_expr_node);
},
else => return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)}),
}
try buf.ensureUnusedCapacity(bytes.len);
try aro.Value.printString(bytes, str_qt, t.comp, buf.writer());

return try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, buf.items));
}

const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const ref = try ZigTag.address_of.create(t.arena, sub_expr_node);
const align_cast = try ZigTag.align_cast.create(t.arena, ref);
break :to_cast try ZigTag.ptr_cast.create(t.arena, align_cast);
},
.int_to_pointer => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
break :to_cast try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);
},
.int_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
if (sub_expr_node.isBoolRes()) return sub_expr_node;
return ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
},
.float_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
return ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
},
.pointer_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
return ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.null_literal.init() });
},
.bool_to_int => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
break :to_cast try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
},
.bool_to_float => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
break :to_cast try ZigTag.float_from_int.create(t.arena, int_from_bool);
},
.bool_to_pointer => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
break :to_cast try ZigTag.ptr_from_int.create(t.arena, int_from_bool);
},
.float_cast => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
break :to_cast try ZigTag.float_cast.create(t.arena, sub_expr_node);
},
.int_to_float => return t.transExprCoercing(scope, cast.operand, used),
.float_to_int => {
const sub_expr_node = try t.transExprCoercing(scope, cast.operand, .used);
break :to_cast try ZigTag.int_from_float.create(t.arena, sub_expr_node);
},
.pointer_to_int => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
break :to_cast try ZigTag.int_from_ptr.create(t.arena, sub_expr_node);
},
.bitcast => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
if (cast.qt.isPointer(t.comp) and cast.operand.qt(t.tree).isPointer(t.comp)) {
const align_cast = try ZigTag.align_cast.create(t.arena, sub_expr_node);
break :to_cast try ZigTag.ptr_cast.create(t.arena, align_cast);
}
},
else => {},
}
return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)});
};
if (suppress_as == .no_as) return to_cast;
const as = try ZigTag.as.create(t.arena, .{
.lhs = try t.transType(scope, cast.qt, cast.l_paren),
.rhs = to_cast,
});
return as;
}

fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {
Expand Down
11 changes: 11 additions & 0 deletions test/cases/translate/c_style_cast.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@ int int_from_float(float a) {
return (int)a;
}

int add_int_from_float(float a, float b) {
return (int)a + (int) b;
}

// translate
//
// pub export fn int_from_float(arg_a: f32) c_int {
// var a = arg_a;
// _ = &a;
// return @intFromFloat(a);
// }
// pub export fn add_int_from_float(arg_a: f32, arg_b: f32) c_int {
// var a = arg_a;
// _ = &a;
// var b = arg_b;
// _ = &b;
// return @as(c_int, @intFromFloat(a)) + @as(c_int, @intFromFloat(b));
// }
20 changes: 20 additions & 0 deletions test/cases/translate/cast_array_to_pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
void foo(void) {
int a[10];
int* x = a;

char b[6];
char* y = b;
}

// translate
//
// pub export fn foo() void {
// var a: [10]c_int = undefined;
// _ = &a;
// var x: [*c]c_int = @ptrCast(@alignCast(&a));
// _ = &x;
// var b: [6]u8 = undefined;
// _ = &b;
// var y: [*c]u8 = @ptrCast(@alignCast(&b));
// _ = &y;
// }
3 changes: 1 addition & 2 deletions test/cases/translate/casting_pointer_to_pointer.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ float **ptrptrcast() {
}

// translate
// expect=fail
//
// pub export fn ptrptrcast() [*c][*c]f32 {
// var a: [*c][*c]c_int = undefined;
// _ = &a;
// return @as([*c][*c]f32, @ptrCast(@alignCast(a)));
// return @ptrCast(@alignCast(a));
// }
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ void bar(void) {
}

// translate
// expect=fail
//
// pub extern fn foo() void;
// pub export fn bar() void {
// var func_ptr: ?*anyopaque = @as(?*anyopaque, @ptrCast(&foo));
// var func_ptr: ?*anyopaque = &foo;
// _ = &func_ptr;
// var typed_func_ptr: ?*const fn () callconv(.c) void = @as(?*const fn () callconv(.c) void, @ptrFromInt(@as(c_ulong, @intCast(@intFromPtr(func_ptr)))));
// var typed_func_ptr: ?*const fn () callconv(.c) void = @ptrFromInt(@as(c_ulong, @intFromPtr(func_ptr)));
// _ = &typed_func_ptr;
// }
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
void foo(void) {
int x = 23;
int y = (int*)y;
}

// translate
//
// pub export fn foo() void {
// var x: c_int = 23;
// _ = &x;
// var y: c_int = @intFromPtr(@as([*c]c_int, @ptrFromInt(y)));
// _ = &y;
// }
36 changes: 36 additions & 0 deletions test/cases/translate/casting_to_and_from_bool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
int foo(void) {
int v1;
_Bool b1 = v1;
double v2;
_Bool b2 = v2;
void* v3;
_Bool b3 = v3;

int v4 = b1;
double v5 = b2;
void* v6 = b3;
}

// translate
//
// pub export fn foo() c_int {
// var v1: c_int = undefined;
// _ = &v1;
// var b1: bool = v1 != 0;
// _ = &b1;
// var v2: f64 = undefined;
// _ = &v2;
// var b2: bool = v2 != 0;
// _ = &b2;
// var v3: ?*anyopaque = undefined;
// _ = &v3;
// var b3: bool = v3 != null;
// _ = &b3;
// var v4: c_int = @intFromBool(b1);
// _ = &v4;
// var v5: f64 = @floatFromInt(@intFromBool(b2));
// _ = &v5;
// var v6: ?*anyopaque = @ptrFromInt(@intFromBool(b3));
// _ = &v6;
// return undefined;
// }
1 change: 0 additions & 1 deletion test/cases/translate/null_pointer_implicit_cast.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ int* foo(void) {
}

// translate
// expect=fail
//
// pub export fn foo() [*c]c_int {
// return null;
Expand Down
3 changes: 1 addition & 2 deletions test/cases/translate/pointer_casting.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ float *ptrcast() {
}

// translate
// expect=fail
//
// pub export fn ptrcast() [*c]f32 {
// var a: [*c]c_int = undefined;
// _ = &a;
// return @as([*c]f32, @ptrCast(@alignCast(a)));
// return @ptrCast(@alignCast(a));
// }

0 comments on commit 8eedcf4

Please sign in to comment.