diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 381673e704..a3db8f7c2b 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1040,3 +1040,12 @@ pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClan pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt; pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt; pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt; + +pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr; +pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt; + +pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; +pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; + +pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; +pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index d8fc5ece9c..accbef9214 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -172,7 +172,7 @@ const Scope = struct { // comma operator used return try Block.init(c, scope, true); }, - else => scope = inner, + else => scope = scope.parent.?, } } } @@ -610,7 +610,9 @@ fn transStmt( .ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue), .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used), .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), - .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt), result_used), + .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)), + .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)), + .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)), else => { return revertAndWarn( rp, @@ -1184,7 +1186,6 @@ fn transIfStmt( rp: RestorePoint, scope: *Scope, stmt: *const ZigClangIfStmt, - used: ResultUsed, ) TransError!*ast.Node { // if (c) t // if (c) t else e @@ -1196,7 +1197,7 @@ fn transIfStmt( .id = .Condition, }, }; - if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value); + if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false); _ = try appendToken(rp.c, .RParen, ")"); if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .used, .r_value); @@ -1209,6 +1210,87 @@ fn transIfStmt( return &if_node.base; } +fn transWhileLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangWhileStmt, +) TransError!*ast.Node { + const while_node = try transCreateNodeWhile(rp.c); + + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + while_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + + while_node.body = try transStmt(rp, scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value); + return &while_node.base; +} + +fn transDoWhileLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangDoStmt, +) TransError!*ast.Node { + const while_node = try transCreateNodeWhile(rp.c); + + while_node.condition = try transCreateNodeBoolLiteral(rp.c, true); + _ = try appendToken(rp.c, .RParen, ")"); + var new = false; + + const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) + // there's already a block in C, so we'll append our condition to it. + // c: do { + // c: a; + // c: b; + // c: } while(c); + // zig: while (true) { + // zig: a; + // zig: b; + // zig: if (!cond) break; + // zig: } + (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).? + else blk: { + // the C statement is without a block, so we need to create a block to contain it. + // c: do + // c: a; + // c: while(c); + // zig: while (true) { + // zig: a; + // zig: if (!cond) break; + // zig: } + + new = true; + const block = try transCreateNodeBlock(rp.c, null); + try block.statements.push(try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)); + break :blk block; + }; + + // if (!cond) break; + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + if_node.condition = &prefix_op.base; + if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; + _ = try appendToken(rp.c, .Semicolon, ";"); + + try body_node.statements.push(&if_node.base); + if (new) + body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + while_node.body = &body_node.base; + return &while_node.base; +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -1682,7 +1764,7 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti const builtin_token = try appendToken(c, .Builtin, name); _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.BuiltinCall); - node.* = ast.Node.BuiltinCall{ + node.* = .{ .builtin_token = builtin_token, .params = ast.Node.BuiltinCall.ParamList.init(c.a()), .rparen_token = undefined, // set after appending args @@ -1693,10 +1775,10 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.SuffixOp); - node.* = ast.Node.SuffixOp{ + node.* = .{ .lhs = .{ .node = fn_expr }, - .op = ast.Node.SuffixOp.Op{ - .Call = ast.Node.SuffixOp.Op.Call{ + .op = .{ + .Call = .{ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), .async_token = null, }, @@ -1744,7 +1826,7 @@ fn transCreateNodeInfixOp( if (!grouped) return &node.base; const rparen = try appendToken(rp.c, .RParen, ")"); const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); - grouped_expr.* = ast.Node.GroupedExpression{ + grouped_expr.* = .{ .lparen = lparen, .expr = &node.base, .rparen = rparen, @@ -1776,9 +1858,9 @@ fn transCreateNodePtrType( .Asterisk => try appendToken(c, .Asterisk, "*"), else => unreachable, }; - node.* = ast.Node.PrefixOp{ + node.* = .{ .op_token = op_token, - .op = ast.Node.PrefixOp.Op{ + .op = .{ .PtrType = .{ .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, @@ -1811,7 +1893,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { const ltoken = try appendToken(c, .Keyword_return, "return"); const node = try c.a().create(ast.Node.ControlFlowExpression); - node.* = ast.Node.ControlFlowExpression{ + node.* = .{ .ltoken = ltoken, .kind = .Return, .rhs = null, @@ -1822,7 +1904,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { const token = try appendToken(c, .Keyword_undefined, "undefined"); const node = try c.a().create(ast.Node.UndefinedLiteral); - node.* = ast.Node.UndefinedLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -1831,7 +1913,7 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { fn transCreateNodeNullLiteral(c: *Context) !*ast.Node { const token = try appendToken(c, .Keyword_null, "null"); const node = try c.a().create(ast.Node.NullLiteral); - node.* = ast.Node.NullLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -1843,7 +1925,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { else try appendToken(c, .Keyword_false, "false"); const node = try c.a().create(ast.Node.BoolLiteral); - node.* = ast.Node.BoolLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -2032,7 +2114,7 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE break :blk try transCreateNodeIdentifier(c, l); } else null; const node = try c.a().create(ast.Node.ControlFlowExpression); - node.* = ast.Node.ControlFlowExpression{ + node.* = .{ .ltoken = ltoken, .kind = .{ .Break = label_node }, .rhs = null, @@ -2046,7 +2128,7 @@ fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []con const name_tok = try appendIdentifier(c, name); const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ + node.* = .{ .doc_comments = null, .visib_token = visib_tok, .thread_local_token = null, @@ -2065,6 +2147,24 @@ fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []con return node; } +fn transCreateNodeWhile(c: *Context) !*ast.Node.While { + const while_tok = try appendToken(c, .Keyword_while, "while"); + _ = try appendToken(c, .LParen, "("); + + const node = try c.a().create(ast.Node.While); + node.* = .{ + .label = null, + .inline_token = null, + .while_token = while_tok, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, + }; + return node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, diff --git a/test/translate_c.zig b/test/translate_c.zig index c97ccfa6ab..6b2014db9b 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -755,6 +755,46 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("while loops", + \\int foo() { + \\ int a = 5; + \\ while (2) + \\ a = 2; + \\ while (4) { + \\ int a = 4; + \\ a = 9; + \\ return 6, a; + \\ } + \\ do { + \\ int a = 2; + \\ a = 12; + \\ } while (4); + \\ do + \\ a = 7; + \\ while (4); + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = 5; + \\ while (2 != 0) a = 2; + \\ while (4 != 0) { + \\ var a: c_int = 4; + \\ a = 9; + \\ _ = 6; + \\ return a; + \\ } + \\ while (true) { + \\ var a: c_int = 2; + \\ a = 12; + \\ if (!4 != 0) break; + \\ } + \\ while (true) { + \\ a = 7; + \\ if (!4 != 0) break; + \\ } + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) {