diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-01-01 18:10:43 -0500 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-01-01 18:10:43 -0500 |
| commit | dc28526c6cd9320fefeebe15a90dd9269cf2b2ca (patch) | |
| tree | b44dded33d9e911e4de4024a9c9ea75378416c66 | |
| parent | 5575e2a168c07d2dcc0e58146231e490ef8a898e (diff) | |
| download | zig-dc28526c6cd9320fefeebe15a90dd9269cf2b2ca.tar.gz zig-dc28526c6cd9320fefeebe15a90dd9269cf2b2ca.zip | |
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.
| -rw-r--r-- | src-self-hosted/clang.zig | 4 | ||||
| -rw-r--r-- | src-self-hosted/translate_c.zig | 202 | ||||
| -rw-r--r-- | test/translate_c.zig | 262 |
3 files changed, 317 insertions, 151 deletions
diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index e8670af70e..d461a8e786 100644 --- a/src-self-hosted/clang.zig +++ b/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; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 0a7917fa5e..84c3cbeaa9 100644 --- a/src-self-hosted/translate_c.zig +++ b/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, .{ diff --git a/test/translate_c.zig b/test/translate_c.zig index 728bfd9869..96e9a9a65c 100644 --- a/test/translate_c.zig +++ b/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)); + \\} + }); } |
