Skip to content
This repository was archived by the owner on Nov 26, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
// }