| ... | @@ -78,6 +78,10 @@ const Scope = struct { | ... | @@ -78,6 +78,10 @@ const Scope = struct { |
| 78 | mangle_count: u32 = 0, | 78 | mangle_count: u32 = 0, |
| 79 | lbrace: ast.TokenIndex, | 79 | lbrace: ast.TokenIndex, |
| 80 | | 80 | |
| | 81 | /// When the block corresponds to a function, keep track of the return type |
| | 82 | /// so that the return expression can be cast, if necessary |
| | 83 | return_type: ?clang.QualType = null, |
| | 84 | |
| 81 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { | 85 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { |
| 82 | var blk = Block{ | 86 | var blk = Block{ |
| 83 | .base = .{ | 87 | .base = .{ |
| ... | @@ -209,6 +213,21 @@ const Scope = struct { | ... | @@ -209,6 +213,21 @@ const Scope = struct { |
| 209 | } | 213 | } |
| 210 | } | 214 | } |
| 211 | | 215 | |
| | 216 | fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType { |
| | 217 | var scope = inner; |
| | 218 | while (true) { |
| | 219 | switch (scope.id) { |
| | 220 | .Root => return null, |
| | 221 | .Block => { |
| | 222 | const block = @fieldParentPtr(Block, "base", scope); |
| | 223 | if (block.return_type) |qt| return qt; |
| | 224 | scope = scope.parent.?; |
| | 225 | }, |
| | 226 | else => scope = scope.parent.?, |
| | 227 | } |
| | 228 | } |
| | 229 | } |
| | 230 | |
| 212 | fn getAlias(scope: *Scope, name: []const u8) []const u8 { | 231 | fn getAlias(scope: *Scope, name: []const u8) []const u8 { |
| 213 | return switch (scope.id) { | 232 | return switch (scope.id) { |
| 214 | .Root => return name, | 233 | .Root => return name, |
| ... | @@ -580,6 +599,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -580,6 +599,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 580 | else => break fn_type, | 599 | else => break fn_type, |
| 581 | } | 600 | } |
| 582 | } else unreachable; | 601 | } else unreachable; |
| | 602 | const fn_ty = @ptrCast(*const clang.FunctionType, fn_type); |
| | 603 | const return_qt = fn_ty.getReturnType(); |
| 583 | | 604 | |
| 584 | const proto_node = switch (fn_type.getTypeClass()) { | 605 | const proto_node = switch (fn_type.getTypeClass()) { |
| 585 | .FunctionProto => blk: { | 606 | .FunctionProto => blk: { |
| ... | @@ -617,7 +638,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -617,7 +638,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 617 | // actual function definition with body | 638 | // actual function definition with body |
| 618 | const body_stmt = fn_decl.getBody(); | 639 | const body_stmt = fn_decl.getBody(); |
| 619 | var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false); | 640 | var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false); |
| | 641 | block_scope.return_type = return_qt; |
| 620 | defer block_scope.deinit(); | 642 | defer block_scope.deinit(); |
| | 643 | |
| 621 | var scope = &block_scope.base; | 644 | var scope = &block_scope.base; |
| 622 | | 645 | |
| 623 | var param_id: c_uint = 0; | 646 | var param_id: c_uint = 0; |
| ... | @@ -667,10 +690,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -667,10 +690,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 667 | }; | 690 | }; |
| 668 | // add return statement if the function didn't have one | 691 | // add return statement if the function didn't have one |
| 669 | blk: { | 692 | blk: { |
| 670 | const fn_ty = @ptrCast(*const clang.FunctionType, fn_type); | | |
| 671 | | | |
| 672 | if (fn_ty.getNoReturnAttr()) break :blk; | 693 | if (fn_ty.getNoReturnAttr()) break :blk; |
| 673 | const return_qt = fn_ty.getReturnType(); | | |
| 674 | if (isCVoid(return_qt)) break :blk; | 694 | if (isCVoid(return_qt)) break :blk; |
| 675 | | 695 | |
| 676 | if (block_scope.statements.items.len > 0) { | 696 | if (block_scope.statements.items.len > 0) { |
| ... | @@ -2018,16 +2038,32 @@ fn transIntegerLiteral( | ... | @@ -2018,16 +2038,32 @@ fn transIntegerLiteral( |
| 2018 | return maybeSuppressResult(rp, scope, result_used, &as_node.base); | 2038 | return maybeSuppressResult(rp, scope, result_used, &as_node.base); |
| 2019 | } | 2039 | } |
| 2020 | | 2040 | |
| | 2041 | /// In C if a function has return type `int` and the return value is a boolean |
| | 2042 | /// expression, there is no implicit cast. So the translated Zig will need to |
| | 2043 | /// call @boolToInt |
| | 2044 | fn zigShouldCastBooleanReturnToInt(node: ?*ast.Node, qt: ?clang.QualType) bool { |
| | 2045 | if (node == null or qt == null) return false; |
| | 2046 | return isBoolRes(node.?) and cIsNativeInt(qt.?); |
| | 2047 | } |
| | 2048 | |
| 2021 | fn transReturnStmt( | 2049 | fn transReturnStmt( |
| 2022 | rp: RestorePoint, | 2050 | rp: RestorePoint, |
| 2023 | scope: *Scope, | 2051 | scope: *Scope, |
| 2024 | expr: *const clang.ReturnStmt, | 2052 | expr: *const clang.ReturnStmt, |
| 2025 | ) TransError!*ast.Node { | 2053 | ) TransError!*ast.Node { |
| 2026 | const return_kw = try appendToken(rp.c, .Keyword_return, "return"); | 2054 | const return_kw = try appendToken(rp.c, .Keyword_return, "return"); |
| 2027 | const rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| | 2055 | var rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| |
| 2028 | try transExprCoercing(rp, scope, val_expr, .used, .r_value) | 2056 | try transExprCoercing(rp, scope, val_expr, .used, .r_value) |
| 2029 | else | 2057 | else |
| 2030 | null; | 2058 | null; |
| | 2059 | const return_qt = scope.findBlockReturnType(rp.c); |
| | 2060 | if (zigShouldCastBooleanReturnToInt(rhs, return_qt)) { |
| | 2061 | const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1); |
| | 2062 | bool_to_int_node.params()[0] = rhs.?; |
| | 2063 | bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")"); |
| | 2064 | |
| | 2065 | rhs = &bool_to_int_node.base; |
| | 2066 | } |
| 2031 | const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ | 2067 | const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ |
| 2032 | .ltoken = return_kw, | 2068 | .ltoken = return_kw, |
| 2033 | .tag = .Return, | 2069 | .tag = .Return, |