translate-c: improve support of integer casting

Widening and truncating integer casting to different signedness
works better now. For example `(unsigned long)-1` is now translated
to zig code that compiles correctly.
This commit is contained in:
Andrew Kelley 2020-01-01 18:10:43 -05:00
parent 5575e2a168
commit dc28526c6c
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
3 changed files with 317 additions and 151 deletions

View File

@ -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;

View File

@ -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, ",");
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,
.{

View File

@ -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));
\\}
});
}