Commit dc28526c6c
Changed files (3)
src-self-hosted
test
src-self-hosted/clang.zig
@@ -804,8 +804,8 @@ pub extern fn ZigClangType_getAsArrayTypeUnsafe(self: *const ZigClangType) *cons
pub extern fn ZigClangStmt_getBeginLoc(self: *const struct_ZigClangStmt) struct_ZigClangSourceLocation;
pub extern fn ZigClangStmt_getStmtClass(self: ?*const struct_ZigClangStmt) ZigClangStmtClass;
pub extern fn ZigClangStmt_classof_Expr(self: ?*const struct_ZigClangStmt) bool;
-pub extern fn ZigClangExpr_getStmtClass(self: ?*const struct_ZigClangExpr) ZigClangStmtClass;
-pub extern fn ZigClangExpr_getType(self: ?*const struct_ZigClangExpr) struct_ZigClangQualType;
+pub extern fn ZigClangExpr_getStmtClass(self: *const struct_ZigClangExpr) ZigClangStmtClass;
+pub extern fn ZigClangExpr_getType(self: *const struct_ZigClangExpr) struct_ZigClangQualType;
pub extern fn ZigClangExpr_getBeginLoc(self: *const struct_ZigClangExpr) struct_ZigClangSourceLocation;
pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitListExpr, i: c_uint) *const ZigClangExpr;
pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr;
src-self-hosted/translate_c.zig
@@ -9,6 +9,7 @@ usingnamespace @import("clang.zig");
const ctok = @import("c_tokenizer.zig");
const CToken = ctok.CToken;
const mem = std.mem;
+const math = std.math;
const CallingConvention = std.builtin.TypeInfo.CallingConvention;
@@ -498,10 +499,13 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
var eq_tok: ast.TokenIndex = undefined;
var init_node: ?*ast.Node = null;
+ // If the initialization expression is not present, initialize with undefined.
+ // If it is an integer literal, we can skip the @as since it will be redundant
+ // with the variable type.
if (ZigClangVarDecl_hasInit(var_decl)) {
eq_tok = try appendToken(c, .Equal, "=");
init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
- transExpr(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) {
+ transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) {
error.UnsupportedTranslation,
error.UnsupportedType,
=> {
@@ -857,7 +861,7 @@ fn transStmt(
.DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)),
.DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue),
.ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt), result_used),
- .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used),
+ .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used, .with_as),
.ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)),
.StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
.ParenExprClass => {
@@ -891,7 +895,7 @@ fn transStmt(
.DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)),
.ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
.PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
- .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used),
+ .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used, .with_as),
.StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used),
.MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used),
.ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used),
@@ -1160,7 +1164,7 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt)
node.eq_token = try appendToken(c, .Equal, "=");
var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
- try transExpr(rp, scope, expr, .used, .r_value)
+ try transExprCoercing(rp, scope, expr, .used, .r_value)
else
try transCreateNodeUndefinedLiteral(c);
if (isBoolRes(init_node)) {
@@ -1391,19 +1395,50 @@ fn finishBoolExpr(
return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
}
+const SuppressCast = enum {
+ with_as,
+ no_as,
+};
fn transIntegerLiteral(
rp: RestorePoint,
scope: *Scope,
expr: *const ZigClangIntegerLiteral,
result_used: ResultUsed,
+ suppress_as: SuppressCast,
) TransError!*ast.Node {
var eval_result: ZigClangExprEvalResult = undefined;
if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) {
const loc = ZigClangIntegerLiteral_getBeginLoc(expr);
return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
}
- const node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
- return maybeSuppressResult(rp, scope, result_used, node);
+
+ if (suppress_as == .no_as) {
+ const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
+ return maybeSuppressResult(rp, scope, result_used, int_lit_node);
+ }
+
+ // Integer literals in C have types, and this can matter for several reasons.
+ // For example, this is valid C:
+ // unsigned char y = 256;
+ // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
+ // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
+ // var y = @bitCast(u8, @truncate(i8, @as(c_int, 256)));
+ // Ideally in translate-c we could flatten this out to simply:
+ // var y: u8 = 0;
+ // But the first step is to be correct, and the next step is to make the output more elegant.
+
+ // @as(T, x)
+ const expr_base = @ptrCast(*const ZigClangExpr, expr);
+ const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+ const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
+ try as_node.params.push(ty_node);
+ _ = try appendToken(rp.c, .Comma, ",");
+
+ const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
+ try as_node.params.push(int_lit_node);
+
+ as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return maybeSuppressResult(rp, scope, result_used, &as_node.base);
}
fn transReturnStmt(
@@ -1413,7 +1448,7 @@ fn transReturnStmt(
) TransError!*ast.Node {
const node = try transCreateNodeReturnExpr(rp.c);
if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| {
- node.rhs = try transExpr(rp, scope, val_expr, .used, .r_value);
+ node.rhs = try transExprCoercing(rp, scope, val_expr, .used, .r_value);
}
_ = try appendToken(rp.c, .Semicolon, ";");
return &node.base;
@@ -1502,11 +1537,41 @@ fn transCCast(
if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
return transCPtrCast(rp, loc, dst_type, src_type, expr);
if (cIsInteger(dst_type) and cIsInteger(src_type)) {
- // @intCast(dest_type, val)
- const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast");
+ // 1. Extend or truncate without changing signed-ness.
+ // 2. Bit-cast to correct signed-ness
+
+ // @bitCast(dest_type, intermediate_value)
+ const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@bitCast");
try cast_node.params.push(try transQualType(rp, dst_type, loc));
_ = try appendToken(rp.c, .Comma, ",");
- try cast_node.params.push(expr);
+
+ switch (cIntTypeCmp(dst_type, src_type)) {
+ .lt => {
+ // @truncate(SameSignSmallerInt, src_type)
+ const trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@truncate");
+ const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type));
+ try trunc_node.params.push(ty_node);
+ _ = try appendToken(rp.c, .Comma, ",");
+ try trunc_node.params.push(expr);
+ trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+ try cast_node.params.push(&trunc_node.base);
+ },
+ .gt => {
+ // @as(SameSignBiggerInt, src_type)
+ const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+ const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type));
+ try as_node.params.push(ty_node);
+ _ = try appendToken(rp.c, .Comma, ",");
+ try as_node.params.push(expr);
+ as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+ try cast_node.params.push(&as_node.base);
+ },
+ .eq => {
+ try cast_node.params.push(expr);
+ },
+ }
cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
return &cast_node.base;
}
@@ -1579,9 +1644,6 @@ fn transCCast(
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
return &builtin_node.base;
}
- // TODO: maybe widen to increase size
- // TODO: maybe bitcast to change sign
- // TODO: maybe truncate to reduce size
const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
try cast_node.params.push(try transQualType(rp, dst_type, loc));
_ = try appendToken(rp.c, .Comma, ",");
@@ -1600,6 +1662,33 @@ fn transExpr(
return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue);
}
+/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
+/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
+fn transExprCoercing(
+ rp: RestorePoint,
+ scope: *Scope,
+ expr: *const ZigClangExpr,
+ used: ResultUsed,
+ lrvalue: LRValue,
+) TransError!*ast.Node {
+ switch (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr))) {
+ .IntegerLiteralClass => {
+ return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, expr), .used, .no_as);
+ },
+ .CharacterLiteralClass => {
+ return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, expr), .used, .no_as);
+ },
+ .UnaryOperatorClass => {
+ const un_expr = @ptrCast(*const ZigClangUnaryOperator, expr);
+ if (ZigClangUnaryOperator_getOpcode(un_expr) == .Extension) {
+ return transExprCoercing(rp, scope, ZigClangUnaryOperator_getSubExpr(un_expr), used, lrvalue);
+ }
+ },
+ else => {},
+ }
+ return transExpr(rp, scope, expr, .used, .r_value);
+}
+
fn transInitListExpr(
rp: RestorePoint,
scope: *Scope,
@@ -1625,7 +1714,7 @@ fn transInitListExpr(
const init_count = ZigClangInitListExpr_getNumInits(expr);
const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, qual_type);
const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
- const all_count = ZigClangAPInt_getLimitedValue(size_ap_int, std.math.maxInt(usize));
+ const all_count = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
const leftover_count = all_count - init_count;
var init_node: *ast.Node.SuffixOp = undefined;
@@ -2033,16 +2122,17 @@ fn transCharLiteral(
scope: *Scope,
stmt: *const ZigClangCharacterLiteral,
result_used: ResultUsed,
+ suppress_as: SuppressCast,
) TransError!*ast.Node {
const kind = ZigClangCharacterLiteral_getKind(stmt);
- switch (kind) {
- .Ascii, .UTF8 => {
+ const int_lit_node = switch (kind) {
+ .Ascii, .UTF8 => blk: {
const val = ZigClangCharacterLiteral_getValue(stmt);
if (kind == .Ascii) {
// C has a somewhat obscure feature called multi-character character
// constant
if (val > 255)
- return transCreateNodeInt(rp.c, val);
+ break :blk try transCreateNodeInt(rp.c, val);
}
var char_buf: [4]u8 = undefined;
const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)});
@@ -2050,7 +2140,7 @@ fn transCharLiteral(
node.* = .{
.token = token,
};
- return maybeSuppressResult(rp, scope, result_used, &node.base);
+ break :blk &node.base;
},
.UTF16, .UTF32, .Wide => return revertAndWarn(
rp,
@@ -2060,7 +2150,22 @@ fn transCharLiteral(
.{kind},
),
else => unreachable,
+ };
+ if (suppress_as == .no_as) {
+ return maybeSuppressResult(rp, scope, result_used, int_lit_node);
}
+ // See comment in `transIntegerLiteral` for why this code is here.
+ // @as(T, x)
+ const expr_base = @ptrCast(*const ZigClangExpr, stmt);
+ const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+ const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
+ try as_node.params.push(ty_node);
+ _ = try appendToken(rp.c, .Comma, ",");
+
+ try as_node.params.push(int_lit_node);
+
+ as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+ return maybeSuppressResult(rp, scope, result_used, &as_node.base);
}
fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node {
@@ -2480,7 +2585,10 @@ fn transCreateCompoundAssign(
// zig: lhs += rhs
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
- var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
+ var rhs_node = if (is_shift)
+ try transExprCoercing(rp, scope, rhs, .used, .r_value)
+ else
+ try transExpr(rp, scope, rhs, .used, .r_value);
if (is_shift) {
const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
@@ -2681,6 +2789,30 @@ fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSou
return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
}
+/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
+/// Asserts the type is an integer.
+fn transQualTypeIntWidthOf(c: *Context, ty: ZigClangQualType, is_signed: bool) TypeError!*ast.Node {
+ return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);
+}
+
+/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.
+/// Asserts the type is an integer.
+fn transTypeIntWidthOf(c: *Context, ty: *const ZigClangType, is_signed: bool) TypeError!*ast.Node {
+ assert(ZigClangType_getTypeClass(ty) == .Builtin);
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
+ return transCreateNodeIdentifier(c, switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",
+ .UShort, .Short => if (is_signed) "c_short" else "c_ushort",
+ .UInt, .Int => if (is_signed) "c_int" else "c_uint",
+ .ULong, .Long => if (is_signed) "c_long" else "c_ulong",
+ .ULongLong, .LongLong => if (is_signed) "c_longlong" else "c_ulonglong",
+ .UInt128, .Int128 => if (is_signed) "i128" else "u128",
+ .Char16 => if (is_signed) "i16" else "u16",
+ .Char32 => if (is_signed) "i32" else "u32",
+ else => unreachable, // only call this function when it has already been determined the type is int
+ });
+}
+
fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool {
const c_type = qualTypeCanon(qt);
if (ZigClangType_getTypeClass(c_type) != .Builtin)
@@ -2742,7 +2874,7 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC
if (int_bit_width != 0) {
// we can perform the log2 now.
- const cast_bit_width = std.math.log2_int(u64, int_bit_width);
+ const cast_bit_width = math.log2_int(u64, int_bit_width);
const node = try rp.c.a().create(ast.Node.IntegerLiteral);
node.* = ast.Node.IntegerLiteral{
.token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}),
@@ -2902,6 +3034,28 @@ fn cIsUnsignedInteger(qt: ZigClangQualType) bool {
};
}
+fn cIntTypeToIndex(qt: ZigClangQualType) u8 {
+ const c_type = qualTypeCanon(qt);
+ assert(ZigClangType_getTypeClass(c_type) == .Builtin);
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
+ return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .Bool, .Char_U, .Char_S, .UChar, .SChar, .Char8 => 1,
+ .WChar_U, .WChar_S => 2,
+ .UShort, .Short, .Char16 => 3,
+ .UInt, .Int, .Char32 => 4,
+ .ULong, .Long => 5,
+ .ULongLong, .LongLong => 6,
+ .UInt128, .Int128 => 7,
+ else => unreachable,
+ };
+}
+
+fn cIntTypeCmp(a: ZigClangQualType, b: ZigClangQualType) math.Order {
+ const a_index = cIntTypeToIndex(a);
+ const b_index = cIntTypeToIndex(b);
+ return math.order(a_index, b_index);
+}
+
fn cIsSignedInteger(qt: ZigClangQualType) bool {
const c_type = qualTypeCanon(qt);
if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
@@ -2946,7 +3100,7 @@ fn transCreateNodeAssign(
if (result_used == .unused) {
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
const eq_token = try appendToken(rp.c, .Equal, "=");
- var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
+ var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value);
if (isBoolRes(rhs_node)) {
const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt");
try builtin_node.params.push(rhs_node);
@@ -3158,7 +3312,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
if (is_negative)
aps_int = ZigClangAPSInt_negate(aps_int);
- var big = try std.math.big.Int.initCapacity(c.a(), num_limbs);
+ var big = try math.big.Int.initCapacity(c.a(), num_limbs);
if (is_negative)
big.negate();
defer big.deinit();
@@ -3522,7 +3676,7 @@ fn transCreateNodeShiftOp(
const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
try as_node.params.push(rhs_type);
_ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value);
+ const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value);
try as_node.params.push(rhs);
as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
@@ -3652,7 +3806,7 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, ty);
const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
- const size = ZigClangAPInt_getLimitedValue(size_ap_int, std.math.maxInt(usize));
+ const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
var node = try transCreateNodePrefixOp(
rp.c,
.{
test/translate_c.zig
@@ -17,13 +17,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ char b = 123;
\\ const int c;
\\ const unsigned d = 440;
+ \\ int e = 10;
+ \\ unsigned int f = 10u;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = undefined;
- \\ var b: u8 = @intCast(u8, 123);
+ \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));
\\ const c: c_int = undefined;
- \\ const d: c_uint = @intCast(c_uint, 440);
+ \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440));
+ \\ var e: c_int = 10;
+ \\ var f: c_uint = 10;
\\}
});
@@ -39,14 +43,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = undefined;
- \\ _ = 1;
+ \\ _ = @as(c_int, 1);
\\ _ = "hey";
- \\ _ = (1 + 1);
- \\ _ = (1 - 1);
+ \\ _ = (@as(c_int, 1) + @as(c_int, 1));
+ \\ _ = (@as(c_int, 1) - @as(c_int, 1));
\\ a = 1;
\\}
});
+ cases.add("function with no prototype",
+ \\int foo() {
+ \\ return 5;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() c_int {
+ \\ return 5;
+ \\}
+ });
+
cases.add("variables",
\\extern int extern_var;
\\static const int int_var = 13;
@@ -474,26 +488,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- cases.add("ignore result, no function arguments",
- \\void foo() {
- \\ int a;
- \\ 1;
- \\ "hey";
- \\ 1 + 1;
- \\ 1 - 1;
- \\ a = 1;
- \\}
- , &[_][]const u8{
- \\pub export fn foo() void {
- \\ var a: c_int = undefined;
- \\ _ = 1;
- \\ _ = "hey";
- \\ _ = (1 + 1);
- \\ _ = (1 - 1);
- \\ a = 1;
- \\}
- });
-
cases.add("constant size array",
\\void func(int array[20]);
, &[_][]const u8{
@@ -582,7 +576,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn foo() void {
\\ {
\\ var i: c_int = 0;
- \\ while (i != 0) : (i = (i + 1)) {}
+ \\ while (i != 0) : (i = (i + @as(c_int, 1))) {}
\\ }
\\}
});
@@ -721,7 +715,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
- \\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1);
+ \\ return (@as(c_int, 1) << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1);
\\}
});
@@ -789,7 +783,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ var a: c_int = undefined;
\\ var b: f32 = undefined;
\\ var c: ?*c_void = undefined;
- \\ return !(a == 0);
+ \\ return !(a == @as(c_int, 0));
\\ return !(a != 0);
\\ return !(b != 0);
\\ return !(c != null);
@@ -864,7 +858,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub fn foo() void {
\\ var arr: [10]u8 = .{
- \\ @intCast(u8, 1),
+ \\ @bitCast(u8, @truncate(i8, @as(c_int, 1))),
\\ } ++ .{0} ** 9;
\\ var arr1: [10][*c]u8 = .{
\\ null,
@@ -1103,18 +1097,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ unsigned d = 440;
\\}
, &[_][]const u8{
- \\pub var a: c_long = @intCast(c_long, 2);
- \\pub var b: c_long = @intCast(c_long, 2);
+ \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));
+ \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));
\\pub var c: c_int = 4;
\\pub export fn foo(arg_c_1: u8) void {
\\ var c_1 = arg_c_1;
\\ var a_2: c_int = undefined;
- \\ var b_3: u8 = @intCast(u8, 123);
- \\ b_3 = @intCast(u8, a_2);
+ \\ var b_3: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));
+ \\ b_3 = @bitCast(u8, @truncate(i8, a_2));
\\ {
\\ var d: c_int = 5;
\\ }
- \\ var d: c_uint = @intCast(c_uint, 440);
+ \\ var d: c_uint = @bitCast(c_uint, @as(c_int, 440));
\\}
});
@@ -1125,11 +1119,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
- \\ _ = 2;
- \\ _ = 4;
- \\ _ = 2;
- \\ _ = 4;
- \\ return 6;
+ \\ _ = @as(c_int, 2);
+ \\ _ = @as(c_int, 4);
+ \\ _ = @as(c_int, 2);
+ \\ _ = @as(c_int, 4);
+ \\ return @as(c_int, 6);
\\}
});
@@ -1144,36 +1138,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ var a: c_int = undefined;
\\ var b: c_int = undefined;
\\ a = blk: {
- \\ const tmp = 2;
+ \\ const tmp = @as(c_int, 2);
\\ b = tmp;
\\ break :blk tmp;
\\ };
\\}
});
- cases.add("if statements",
- \\int foo() {
- \\ if (2) {
- \\ int a = 2;
- \\ }
- \\ if (2, 5) {
- \\ int a = 2;
- \\ }
- \\}
- , &[_][]const u8{
- \\pub export fn foo() c_int {
- \\ if (2 != 0) {
- \\ var a: c_int = 2;
- \\ }
- \\ if ((blk: {
- \\ _ = 2;
- \\ break :blk 5;
- \\ }) != 0) {
- \\ var a: c_int = 2;
- \\ }
- \\}
- });
-
cases.add("while loops",
\\int foo() {
\\ int a = 5;
@@ -1195,21 +1166,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ var a: c_int = 5;
- \\ while (2 != 0) a = 2;
- \\ while (4 != 0) {
+ \\ while (@as(c_int, 2) != 0) a = 2;
+ \\ while (@as(c_int, 4) != 0) {
\\ var a_1: c_int = 4;
\\ a_1 = 9;
- \\ _ = 6;
+ \\ _ = @as(c_int, 6);
\\ return a_1;
\\ }
\\ while (true) {
\\ var a_1: c_int = 2;
\\ a_1 = 12;
- \\ if (!(4 != 0)) break;
+ \\ if (!(@as(c_int, 4) != 0)) break;
\\ }
\\ while (true) {
\\ a = 7;
- \\ if (!(4 != 0)) break;
+ \\ if (!(@as(c_int, 4) != 0)) break;
\\ }
\\}
});
@@ -1227,21 +1198,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ {
\\ var i: c_int = 2;
\\ var b: c_int = 4;
- \\ while ((i + 2) != 0) : (i = 2) {
+ \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) {
\\ var a: c_int = 2;
\\ a = 6;
- \\ _ = 5;
- \\ _ = 7;
+ \\ _ = @as(c_int, 5);
+ \\ _ = @as(c_int, 7);
\\ }
\\ }
- \\ var i: u8 = @intCast(u8, 2);
+ \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2)));
\\}
});
cases.add("shadowing primitive types",
\\unsigned anyerror = 2;
, &[_][]const u8{
- \\pub export var _anyerror: c_uint = @intCast(c_uint, 2);
+ \\pub export var _anyerror: c_uint = @bitCast(c_uint, @as(c_int, 2));
});
cases.add("floats",
@@ -1253,7 +1224,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export var a: f32 = @floatCast(f32, 3.1415);
\\pub export var b: f64 = 3.1415;
\\pub export var c: c_int = @floatToInt(c_int, 3.1415);
- \\pub export var d: f64 = @intToFloat(f64, 3);
+ \\pub export var d: f64 = @intToFloat(f64, @as(c_int, 3));
});
cases.add("conditional operator",
@@ -1263,8 +1234,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
, &[_][]const u8{
\\pub export fn bar() c_int {
- \\ if ((if (2 != 0) 5 else (if (5 != 0) 4 else 6)) != 0) _ = 2;
- \\ return if (2 != 0) 5 else if (5 != 0) 4 else 6;
+ \\ if ((if (@as(c_int, 2) != 0) @as(c_int, 5) else (if (@as(c_int, 5) != 0) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2);
+ \\ return if (@as(c_int, 2) != 0) @as(c_int, 5) else if (@as(c_int, 5) != 0) @as(c_int, 4) else @as(c_int, 6);
\\}
});
@@ -1303,7 +1274,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ }
\\ res = 2;
\\ }
- \\ res = (3 * i);
+ \\ res = (@as(c_int, 3) * i);
\\ break :__switch;
\\ }
\\ res = 5;
@@ -1397,6 +1368,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
+ // TODO translate-c should in theory be able to figure out to drop all these casts
cases.add("escape sequences",
\\const char *escapes() {
\\char a = '\'',
@@ -1415,17 +1387,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\
, &[_][]const u8{
\\pub export fn escapes() [*c]const u8 {
- \\ var a: u8 = @intCast(u8, '\'');
- \\ var b: u8 = @intCast(u8, '\\');
- \\ var c: u8 = @intCast(u8, '\x07');
- \\ var d: u8 = @intCast(u8, '\x08');
- \\ var e: u8 = @intCast(u8, '\x0c');
- \\ var f: u8 = @intCast(u8, '\n');
- \\ var g: u8 = @intCast(u8, '\r');
- \\ var h: u8 = @intCast(u8, '\t');
- \\ var i: u8 = @intCast(u8, '\x0b');
- \\ var j: u8 = @intCast(u8, '\x00');
- \\ var k: u8 = @intCast(u8, '\"');
+ \\ var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\'')));
+ \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\')));
+ \\ var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07')));
+ \\ var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08')));
+ \\ var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c')));
+ \\ var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n')));
+ \\ var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r')));
+ \\ var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t')));
+ \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b')));
+ \\ var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00')));
+ \\ var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"')));
\\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
});
@@ -1446,12 +1418,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ while (true) {
- \\ a = (a - 1);
+ \\ a = (a - @as(c_int, 1));
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ while (true) {
- \\ b = (b - 1);
+ \\ b = (b - @as(c_int, 1));
\\ if (!(b != 0)) break;
\\ }
\\}
@@ -1602,7 +1574,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const yes = [*c]u8;
\\pub export fn foo() void {
\\ var a: yes = undefined;
- \\ if (a != null) _ = 2;
+ \\ if (a != null) _ = @as(c_int, 2);
\\}
});
@@ -1695,7 +1667,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- cases.add("if statement",
+ cases.add("simple if statement",
\\int max(int a, int b) {
\\ if (a < b)
\\ return b;
@@ -1717,6 +1689,29 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
+ cases.add("if statements",
+ \\int foo() {
+ \\ if (2) {
+ \\ int a = 2;
+ \\ }
+ \\ if (2, 5) {
+ \\ int a = 2;
+ \\ }
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() c_int {
+ \\ if (@as(c_int, 2) != 0) {
+ \\ var a: c_int = 2;
+ \\ }
+ \\ if ((blk: {
+ \\ _ = @as(c_int, 2);
+ \\ break :blk @as(c_int, 5);
+ \\ }) != 0) {
+ \\ var a: c_int = 2;
+ \\ }
+ \\}
+ });
+
cases.add("if on non-bool",
\\enum SomeEnum { A, B, C };
\\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
@@ -1764,7 +1759,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub export fn abs(arg_a: c_int) c_int {
\\ var a = arg_a;
- \\ return if (a < 0) -a else a;
+ \\ return if (a < @as(c_int, 0)) -a else a;
\\}
});
@@ -1845,7 +1840,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub export fn foo() void {
\\ var i: c_int = 0;
- \\ var u: c_uint = @intCast(c_uint, 0);
+ \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0));
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
@@ -1885,7 +1880,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn log2(arg_a: c_uint) c_int {
\\ var a = arg_a;
\\ var i: c_int = 0;
- \\ while (a > @intCast(c_uint, 0)) {
+ \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
\\ a >>= @as(@import("std").math.Log2Int(c_int), 1);
\\ }
\\ return i;
@@ -1905,7 +1900,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn log2(arg_a: u32) c_int {
\\ var a = arg_a;
\\ var i: c_int = 0;
- \\ while (a > @intCast(c_uint, 0)) {
+ \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
\\ a >>= @as(@import("std").math.Log2Int(c_int), 1);
\\ }
\\ return i;
@@ -1929,42 +1924,42 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ var a: c_int = 0;
\\ a += (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* + 1;
+ \\ ref.* = ref.* + @as(c_int, 1);
\\ break :blk ref.*;
\\ });
\\ a -= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* - 1;
+ \\ ref.* = ref.* - @as(c_int, 1);
\\ break :blk ref.*;
\\ });
\\ a *= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* * 1;
+ \\ ref.* = ref.* * @as(c_int, 1);
\\ break :blk ref.*;
\\ });
\\ a &= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* & 1;
+ \\ ref.* = ref.* & @as(c_int, 1);
\\ break :blk ref.*;
\\ });
\\ a |= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* | 1;
+ \\ ref.* = ref.* | @as(c_int, 1);
\\ break :blk ref.*;
\\ });
\\ a ^= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* ^ 1;
+ \\ ref.* = ref.* ^ @as(c_int, 1);
\\ break :blk ref.*;
\\ });
\\ a >>= @as(@import("std").math.Log2Int(c_int), (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* >> @as(@import("std").math.Log2Int(c_int), 1);
+ \\ ref.* = ref.* >> @as(@import("std").math.Log2Int(c_int), @as(c_int, 1));
\\ break :blk ref.*;
\\ }));
\\ a <<= @as(@import("std").math.Log2Int(c_int), (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* << @as(@import("std").math.Log2Int(c_int), 1);
+ \\ ref.* = ref.* << @as(@import("std").math.Log2Int(c_int), @as(c_int, 1));
\\ break :blk ref.*;
\\ }));
\\}
@@ -1984,45 +1979,45 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
, &[_][]const u8{
\\pub export fn foo() void {
- \\ var a: c_uint = @intCast(c_uint, 0);
+ \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0));
\\ a +%= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* +% @intCast(c_uint, 1);
+ \\ ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a -%= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* -% @intCast(c_uint, 1);
+ \\ ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a *%= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* *% @intCast(c_uint, 1);
+ \\ ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a &= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* & @intCast(c_uint, 1);
+ \\ ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a |= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* | @intCast(c_uint, 1);
+ \\ ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a ^= (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* ^ @intCast(c_uint, 1);
+ \\ ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a >>= @as(@import("std").math.Log2Int(c_uint), (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* >> @as(@import("std").math.Log2Int(c_int), 1);
+ \\ ref.* = ref.* >> @as(@import("std").math.Log2Int(c_int), @as(c_int, 1));
\\ break :blk ref.*;
\\ }));
\\ a <<= @as(@import("std").math.Log2Int(c_uint), (blk: {
\\ const ref = &a;
- \\ ref.* = ref.* << @as(@import("std").math.Log2Int(c_int), 1);
+ \\ ref.* = ref.* << @as(@import("std").math.Log2Int(c_int), @as(c_int, 1));
\\ break :blk ref.*;
\\ }));
\\}
@@ -2044,7 +2039,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub export fn foo() void {
\\ var i: c_int = 0;
- \\ var u: c_uint = @intCast(c_uint, 0);
+ \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0));
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
@@ -2115,19 +2110,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ fn_int(@floatToInt(c_int, 3));
\\ fn_int(@floatToInt(c_int, 3));
\\ fn_int(@floatToInt(c_int, 3));
- \\ fn_int(1094861636);
- \\ fn_f32(@intToFloat(f32, 3));
- \\ fn_f64(@intToFloat(f64, 3));
- \\ fn_char(@intCast(u8, '3'));
- \\ fn_char(@intCast(u8, '\x01'));
- \\ fn_char(@intCast(u8, 0));
+ \\ fn_int(@as(c_int, 1094861636));
+ \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
+ \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));
+ \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));
+ \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));
+ \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));
\\ fn_f32(3);
\\ fn_f64(3);
- \\ fn_bool(123 != 0);
- \\ fn_bool(0 != 0);
+ \\ fn_bool(@as(c_int, 123) != 0);
+ \\ fn_bool(@as(c_int, 0) != 0);
\\ fn_bool(@ptrToInt(&fn_int) != 0);
\\ fn_int(@intCast(c_int, @ptrToInt(&fn_int)));
- \\ fn_ptr(@intToPtr(?*c_void, 42));
+ \\ fn_ptr(@intToPtr(?*c_void, @as(c_int, 42)));
\\}
});
@@ -2223,8 +2218,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
, &[_][]const u8{
\\pub fn foo() void {
- \\ if (1 != 0) while (true) {
- \\ if (!(0 != 0)) break;
+ \\ if (@as(c_int, 1) != 0) while (true) {
+ \\ if (!(@as(c_int, 0) != 0)) break;
\\ };
\\}
});
@@ -2263,4 +2258,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ };
\\}
});
+
+ cases.add("widening and truncating integer casting to different signedness",
+ \\unsigned long foo(void) {
+ \\ return -1;
+ \\}
+ \\unsigned short bar(long x) {
+ \\ return x;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() c_ulong {
+ \\ return @bitCast(c_ulong, @as(c_long, -@as(c_int, 1)));
+ \\}
+ \\pub export fn bar(arg_x: c_long) c_ushort {
+ \\ var x = arg_x;
+ \\ return @bitCast(c_ushort, @truncate(c_short, x));
+ \\}
+ });
}