authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-02-17 22:11:26+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-02-17 22:11:26+02:00
log7ca53bdfaab59e61c38d0bedb6b16739904f7519
treeb7678de14625868401c95001a77e697cb8fffa66
parent3717bedb4e3198fe2ded167b41f9b0441e817b9c
signature Commit is signed but in an unrecognized format.

translate-c: improve switch translation


4 files changed, 293 insertions(+), 259 deletions(-)

src/clang.zig+3-3
......@@ -273,12 +273,12 @@ pub const CompoundAssignOperator = opaque {
273273
274274pub const CompoundStmt = opaque {
275275 pub const body_begin = ZigClangCompoundStmt_body_begin;
276 extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) const_body_iterator;
276 extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) ConstBodyIterator;
277277
278278 pub const body_end = ZigClangCompoundStmt_body_end;
279 extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) const_body_iterator;
279 extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) ConstBodyIterator;
280280
281 pub const const_body_iterator = [*]const *Stmt;
281 pub const ConstBodyIterator = [*]const *Stmt;
282282};
283283
284284pub const ConditionalOperator = opaque {};
src/translate_c.zig+179-180
......@@ -31,7 +31,6 @@ const Scope = struct {
3131 parent: ?*Scope,
3232
3333 const Id = enum {
34 @"switch",
3534 block,
3635 root,
3736 condition,
......@@ -39,17 +38,6 @@ const Scope = struct {
3938 do_loop,
4039 };
4140
42 /// Represents an in-progress Node.Switch. This struct is stack-allocated.
43 /// When it is deinitialized, it produces an Node.Switch which is allocated
44 /// into the main arena.
45 const Switch = struct {
46 base: Scope,
47 pending_block: Block,
48 cases: std.ArrayList(Node),
49 switch_label: ?[]const u8,
50 default_label: ?[]const u8,
51 };
52
5341 /// Used for the scope of condition expressions, for example `if (cond)`.
5442 /// The block is lazily initialised because it is only needed for rare
5543 /// cases of comma operators being used.
......@@ -230,7 +218,7 @@ const Scope = struct {
230218 return switch (scope.id) {
231219 .root => return name,
232220 .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
233 .@"switch", .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
221 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
234222 };
235223 }
236224
......@@ -238,7 +226,7 @@ const Scope = struct {
238226 return switch (scope.id) {
239227 .root => @fieldParentPtr(Root, "base", scope).contains(name),
240228 .block => @fieldParentPtr(Block, "base", scope).contains(name),
241 .@"switch", .loop, .do_loop, .condition => scope.parent.?.contains(name),
229 .loop, .do_loop, .condition => scope.parent.?.contains(name),
242230 };
243231 }
244232
......@@ -247,24 +235,12 @@ const Scope = struct {
247235 while (true) {
248236 switch (scope.id) {
249237 .root => unreachable,
250 .@"switch" => return scope,
251238 .loop, .do_loop => return scope,
252239 else => scope = scope.parent.?,
253240 }
254241 }
255242 }
256243
257 fn getSwitch(inner: *Scope) *Scope.Switch {
258 var scope = inner;
259 while (true) {
260 switch (scope.id) {
261 .root => unreachable,
262 .@"switch" => return @fieldParentPtr(Switch, "base", scope),
263 else => scope = scope.parent.?,
264 }
265 }
266 }
267
268244 /// Appends a node to the first block scope if inside a function, or to the root tree if not.
269245 fn appendNode(inner: *Scope, node: Node) !void {
270246 var scope = inner;
......@@ -570,7 +546,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
570546 }
571547
572548 const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt);
573 transCompoundStmtInline(c, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
549 transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) {
574550 error.OutOfMemory => |e| return e,
575551 error.UnsupportedTranslation,
576552 error.UnsupportedType,
......@@ -583,24 +559,10 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
583559 };
584560 // add return statement if the function didn't have one
585561 blk: {
586 if (fn_ty.getNoReturnAttr()) break :blk;
587 if (isCVoid(return_qt)) break :blk;
588
589 if (block_scope.statements.items.len > 0) {
590 var last = block_scope.statements.items[block_scope.statements.items.len - 1];
591 while (true) {
592 switch (last.tag()) {
593 .block => {
594 const block = last.castTag(.block).?;
595 if (block.data.stmts.len == 0) break;
596
597 last = block.data.stmts[block.data.stmts.len - 1];
598 },
599 // no extra return needed
600 .@"return", .return_void => break :blk,
601 else => break,
602 }
603 }
562 const maybe_body = try block_scope.complete(c);
563 if (fn_ty.getNoReturnAttr() or isCVoid(return_qt) or maybe_body.isNoreturn(false)) {
564 proto_node.data.body = maybe_body;
565 break :blk;
604566 }
605567
606568 const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
......@@ -616,9 +578,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
616578 };
617579 const ret = try Tag.@"return".create(c.arena, rhs);
618580 try block_scope.statements.append(ret);
581 proto_node.data.body = try block_scope.complete(c);
619582 }
620583
621 proto_node.data.body = try block_scope.complete(c);
622584 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
623585}
624586
......@@ -1079,7 +1041,7 @@ fn transStmt(
10791041 return Tag.empty_block.init();
10801042 },
10811043 .ContinueStmtClass => return Tag.@"continue".init(),
1082 .BreakStmtClass => return transBreak(c, scope),
1044 .BreakStmtClass => return Tag.@"break".init(),
10831045 .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)),
10841046 .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
10851047 .ConditionalOperatorClass => {
......@@ -1089,8 +1051,9 @@ fn transStmt(
10891051 return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);
10901052 },
10911053 .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)),
1092 .CaseStmtClass => return transCase(c, scope, @ptrCast(*const clang.CaseStmt, stmt)),
1093 .DefaultStmtClass => return transDefault(c, scope, @ptrCast(*const clang.DefaultStmt, stmt)),
1054 .CaseStmtClass, .DefaultStmtClass => {
1055 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{});
1056 },
10941057 .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
10951058 .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),
10961059 .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),
......@@ -1107,13 +1070,7 @@ fn transStmt(
11071070 return maybeSuppressResult(c, scope, result_used, expr);
11081071 },
11091072 else => {
1110 return fail(
1111 c,
1112 error.UnsupportedTranslation,
1113 stmt.getBeginLoc(),
1114 "TODO implement translation of stmt class {s}",
1115 .{@tagName(sc)},
1116 );
1073 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
11171074 },
11181075 }
11191076}
......@@ -1255,14 +1212,13 @@ fn transBinaryOperator(
12551212
12561213fn transCompoundStmtInline(
12571214 c: *Context,
1258 parent_scope: *Scope,
12591215 stmt: *const clang.CompoundStmt,
12601216 block: *Scope.Block,
12611217) TransError!void {
12621218 var it = stmt.body_begin();
12631219 const end_it = stmt.body_end();
12641220 while (it != end_it) : (it += 1) {
1265 const result = try transStmt(c, parent_scope, it[0], .unused);
1221 const result = try transStmt(c, &block.base, it[0], .unused);
12661222 if (result.tag() == .declaration) continue;
12671223 try block.statements.append(result);
12681224 }
......@@ -1271,7 +1227,7 @@ fn transCompoundStmtInline(
12711227fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node {
12721228 var block_scope = try Scope.Block.init(c, scope, false);
12731229 defer block_scope.deinit();
1274 try transCompoundStmtInline(c, &block_scope.base, stmt, &block_scope);
1230 try transCompoundStmtInline(c, stmt, &block_scope);
12751231 return try block_scope.complete(c);
12761232}
12771233
......@@ -2162,7 +2118,7 @@ fn transDoWhileLoop(
21622118 defer cond_scope.deinit();
21632119 const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used);
21642120 const if_not_break = switch (cond.tag()) {
2165 .false_literal => try Tag.@"break".create(c.arena, null),
2121 .false_literal => Tag.@"break".init(),
21662122 .true_literal => {
21672123 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);
21682124 return Tag.while_true.create(c.arena, body_node);
......@@ -2263,133 +2219,189 @@ fn transSwitch(
22632219 };
22642220 defer cond_scope.deinit();
22652221 const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used);
2266 const switch_node = try c.arena.create(ast.Payload.Switch);
2267 switch_node.* = .{
2268 .base = .{ .tag = .@"switch" },
2269 .data = .{
2270 .cond = switch_expr,
2271 .cases = undefined, // set later
2272 },
2273 };
2274
2275 var switch_scope = Scope.Switch{
2276 .base = .{
2277 .id = .@"switch",
2278 .parent = scope,
2279 },
2280 .cases = std.ArrayList(Node).init(c.gpa),
2281 .pending_block = undefined,
2282 .default_label = null,
2283 .switch_label = null,
2284 };
2285 defer switch_scope.cases.deinit();
22862222
2287 // tmp block that all statements will go before being picked up by a case or default
2288 var block_scope = try Scope.Block.init(c, &switch_scope.base, false);
2289 defer block_scope.deinit();
2290
2291 // Note that we do not defer a deinit here; the switch_scope.pending_block field
2292 // has its own memory management. This resource is freed inside `transCase` and
2293 // then the final pending_block is freed at the bottom of this function with
2294 // pending_block.deinit().
2295 switch_scope.pending_block = try Scope.Block.init(c, scope, false);
2296 try switch_scope.pending_block.statements.append(Node.initPayload(&switch_node.base));
2223 var cases = std.ArrayList(Node).init(c.gpa);
2224 defer cases.deinit();
2225 var has_default = false;
2226
2227 const body = stmt.getBody();
2228 assert(body.getStmtClass() == .CompoundStmtClass);
2229 const compound_stmt = @ptrCast(*const clang.CompoundStmt, body);
2230 var it = compound_stmt.body_begin();
2231 const end_it = compound_stmt.body_end();
2232 // Iterate over switch body and collect all cases.
2233 // Fallthrough is handled by duplicating statements.
2234 while (it != end_it) : (it += 1) {
2235 switch (it[0].getStmtClass()) {
2236 .CaseStmtClass => {
2237 var items = std.ArrayList(Node).init(c.gpa);
2238 defer items.deinit();
2239 const sub = try transCaseStmt(c, scope, it[0], &items);
2240 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
2241
2242 if (items.items.len == 0) {
2243 has_default = true;
2244 const switch_else = try Tag.switch_else.create(c.arena, res);
2245 try cases.append(switch_else);
2246 } else {
2247 const switch_prong = try Tag.switch_prong.create(c.arena, .{
2248 .cases = try c.arena.dupe(Node, items.items),
2249 .cond = res,
2250 });
2251 try cases.append(switch_prong);
2252 }
2253 },
2254 .DefaultStmtClass => {
2255 has_default = true;
2256 const default_stmt = @ptrCast(*const clang.DefaultStmt, it[0]);
2257
2258 var sub = default_stmt.getSubStmt();
2259 while (true) switch (sub.getStmtClass()) {
2260 .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(),
2261 .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(),
2262 else => break,
2263 };
22972264
2298 const last = try transStmt(c, &block_scope.base, stmt.getBody(), .unused);
2265 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
22992266
2300 // take all pending statements
2301 const last_block_stmts = last.castTag(.block).?.data.stmts;
2302 try switch_scope.pending_block.statements.ensureCapacity(
2303 switch_scope.pending_block.statements.items.len + last_block_stmts.len,
2304 );
2305 for (last_block_stmts) |n| {
2306 switch_scope.pending_block.statements.appendAssumeCapacity(n);
2267 const switch_else = try Tag.switch_else.create(c.arena, res);
2268 try cases.append(switch_else);
2269 },
2270 else => {}, // collected in transSwitchProngStmt
2271 }
23072272 }
23082273
2309 if (switch_scope.default_label == null) {
2310 switch_scope.switch_label = try block_scope.makeMangledName(c, "switch");
2311 }
2312 if (switch_scope.switch_label) |l| {
2313 switch_scope.pending_block.label = l;
2274 if (!has_default) {
2275 const else_prong = try Tag.switch_else.create(c.arena, Tag.@"break".init());
2276 try cases.append(else_prong);
23142277 }
2315 if (switch_scope.default_label == null) {
2316 const else_prong = try Tag.switch_else.create(
2317 c.arena,
2318 try Tag.@"break".create(c.arena, switch_scope.switch_label.?),
2319 );
2320 try switch_scope.cases.append(else_prong);
2321 }
2322
2323 switch_node.data.cases = try c.arena.dupe(Node, switch_scope.cases.items);
2324 const result_node = try switch_scope.pending_block.complete(c);
2325 switch_scope.pending_block.deinit();
2326 return result_node;
2327}
2328
2329fn transCase(
2330 c: *Context,
2331 scope: *Scope,
2332 stmt: *const clang.CaseStmt,
2333) TransError!Node {
2334 const block_scope = try scope.findBlockScope(c);
2335 const switch_scope = scope.getSwitch();
2336 const label = try block_scope.makeMangledName(c, "case");
2337
2338 const expr = if (stmt.getRHS()) |rhs| blk: {
2339 const lhs_node = try transExpr(c, scope, stmt.getLHS(), .used);
2340 const rhs_node = try transExpr(c, scope, rhs, .used);
2341
2342 break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
2343 } else
2344 try transExpr(c, scope, stmt.getLHS(), .used);
23452278
2346 const switch_prong = try Tag.switch_prong.create(c.arena, .{
2347 .lhs = expr,
2348 .rhs = try Tag.@"break".create(c.arena, label),
2279 return Tag.@"switch".create(c.arena, .{
2280 .cond = switch_expr,
2281 .cases = try c.arena.dupe(Node, cases.items),
23492282 });
2350 try switch_scope.cases.append(switch_prong);
2283}
23512284
2352 switch_scope.pending_block.label = label;
2285/// Collects all items for this case, returns the first statement after the labels.
2286/// If items ends up empty, the prong should be translated as an else.
2287fn transCaseStmt(c: *Context, scope: *Scope, stmt: *const clang.Stmt, items: *std.ArrayList(Node)) TransError!*const clang.Stmt {
2288 var sub = stmt;
2289 var seen_default = false;
2290 while (true) {
2291 switch (sub.getStmtClass()) {
2292 .DefaultStmtClass => {
2293 seen_default = true;
2294 items.items.len = 0;
2295 const default_stmt = @ptrCast(*const clang.DefaultStmt, sub);
2296 sub = default_stmt.getSubStmt();
2297 },
2298 .CaseStmtClass => {
2299 const case_stmt = @ptrCast(*const clang.CaseStmt, sub);
23532300
2354 // take all pending statements
2355 try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
2356 block_scope.statements.shrinkAndFree(0);
2301 if (seen_default) {
2302 items.items.len = 0;
2303 sub = case_stmt.getSubStmt();
2304 continue;
2305 }
23572306
2358 const pending_node = try switch_scope.pending_block.complete(c);
2359 switch_scope.pending_block.deinit();
2360 switch_scope.pending_block = try Scope.Block.init(c, scope, false);
2307 const expr = if (case_stmt.getRHS()) |rhs| blk: {
2308 const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
2309 const rhs_node = try transExprCoercing(c, scope, rhs, .used);
23612310
2362 try switch_scope.pending_block.statements.append(pending_node);
2311 break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
2312 } else
2313 try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
23632314
2364 return transStmt(c, scope, stmt.getSubStmt(), .unused);
2315 try items.append(expr);
2316 sub = case_stmt.getSubStmt();
2317 },
2318 else => return sub,
2319 }
2320 }
23652321}
23662322
2367fn transDefault(
2323/// Collects all statements seen by this case into a block.
2324/// Avoids creating a block if the first statement is a break or return.
2325fn transSwitchProngStmt(
23682326 c: *Context,
23692327 scope: *Scope,
2370 stmt: *const clang.DefaultStmt,
2328 stmt: *const clang.Stmt,
2329 parent_it: clang.CompoundStmt.ConstBodyIterator,
2330 parent_end_it: clang.CompoundStmt.ConstBodyIterator,
23712331) TransError!Node {
2372 const block_scope = try scope.findBlockScope(c);
2373 const switch_scope = scope.getSwitch();
2374 switch_scope.default_label = try block_scope.makeMangledName(c, "default");
2375
2376 const else_prong = try Tag.switch_else.create(
2377 c.arena,
2378 try Tag.@"break".create(c.arena, switch_scope.default_label.?),
2379 );
2380 try switch_scope.cases.append(else_prong);
2381 switch_scope.pending_block.label = switch_scope.default_label.?;
2382
2383 // take all pending statements
2384 try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
2385 block_scope.statements.shrinkAndFree(0);
2332 switch (stmt.getStmtClass()) {
2333 .BreakStmtClass => return Tag.empty_block.init(),
2334 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
2335 .CaseStmtClass, .DefaultStmtClass => unreachable,
2336 else => {
2337 var block_scope = try Scope.Block.init(c, scope, false);
2338 defer block_scope.deinit();
23862339
2387 const pending_node = try switch_scope.pending_block.complete(c);
2388 switch_scope.pending_block.deinit();
2389 switch_scope.pending_block = try Scope.Block.init(c, scope, false);
2390 try switch_scope.pending_block.statements.append(pending_node);
2340 // we do not need to translate `stmt` since it is the first stmt of `parent_it`
2341 try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it);
2342 return try block_scope.complete(c);
2343 },
2344 }
2345}
23912346
2392 return transStmt(c, scope, stmt.getSubStmt(), .unused);
2347/// Collects all statements seen by this case into a block.
2348fn transSwitchProngStmtInline(
2349 c: *Context,
2350 block: *Scope.Block,
2351 start_it: clang.CompoundStmt.ConstBodyIterator,
2352 end_it: clang.CompoundStmt.ConstBodyIterator,
2353) TransError!void {
2354 var it = start_it;
2355 while (it != end_it) : (it += 1) {
2356 switch (it[0].getStmtClass()) {
2357 .ReturnStmtClass => {
2358 const result = try transStmt(c, &block.base, it[0], .unused);
2359 try block.statements.append(result);
2360 return;
2361 },
2362 .BreakStmtClass => return,
2363 .CaseStmtClass => {
2364 var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt();
2365 while (true) switch (sub.getStmtClass()) {
2366 .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(),
2367 .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(),
2368 else => break,
2369 };
2370 const result = try transStmt(c, &block.base, sub, .unused);
2371 assert(result.tag() != .declaration);
2372 try block.statements.append(result);
2373 },
2374 .DefaultStmtClass => {
2375 var sub = @ptrCast(*const clang.DefaultStmt, it[0]).getSubStmt();
2376 while (true) switch (sub.getStmtClass()) {
2377 .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(),
2378 .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(),
2379 else => break,
2380 };
2381 const result = try transStmt(c, &block.base, sub, .unused);
2382 assert(result.tag() != .declaration);
2383 try block.statements.append(result);
2384 },
2385 .CompoundStmtClass => {
2386 const compound_stmt = @ptrCast(*const clang.CompoundStmt, it[0]);
2387 var child_block = try Scope.Block.init(c, &block.base, false);
2388 defer child_block.deinit();
2389
2390 try transCompoundStmtInline(c, compound_stmt, &child_block);
2391 const result = try child_block.complete(c);
2392 try block.statements.append(result);
2393 if (result.isNoreturn(true)) {
2394 return;
2395 }
2396 },
2397 else => {
2398 const result = try transStmt(c, &block.base, it[0], .unused);
2399 if (result.tag() == .declaration) continue;
2400 try block.statements.append(result);
2401 },
2402 }
2403 }
2404 return;
23932405}
23942406
23952407fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
......@@ -3025,19 +3037,6 @@ fn transCPtrCast(
30253037 }
30263038}
30273039
3028fn transBreak(c: *Context, scope: *Scope) TransError!Node {
3029 const break_scope = scope.getBreakableScope();
3030 const label_text: ?[]const u8 = if (break_scope.id == .@"switch") blk: {
3031 const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
3032 const block_scope = try scope.findBlockScope(c);
3033 swtch.switch_label = try block_scope.makeMangledName(c, "switch");
3034 break :blk swtch.switch_label;
3035 } else
3036 null;
3037
3038 return Tag.@"break".create(c.arena, label_text);
3039}
3040
30413040fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
30423041 // TODO use something more accurate
30433042 var dbl = stmt.getValueAsApproximateDouble();
src/translate_c/ast.zig+75-36
......@@ -30,6 +30,7 @@ pub const Node = extern union {
3030 noreturn_type,
3131 @"anytype",
3232 @"continue",
33 @"break",
3334 /// pub usingnamespace @import("std").c.builtins;
3435 usingnamespace_builtins,
3536 // After this, the tag requires a payload.
......@@ -48,9 +49,8 @@ pub const Node = extern union {
4849 @"switch",
4950 /// else => operand,
5051 switch_else,
51 /// lhs => rhs,
52 /// items => body,
5253 switch_prong,
53 @"break",
5454 break_val,
5555 @"return",
5656 field_access,
......@@ -219,6 +219,7 @@ pub const Node = extern union {
219219 .noreturn_type,
220220 .@"anytype",
221221 .@"continue",
222 .@"break",
222223 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
223224
224225 .std_mem_zeroes,
......@@ -294,7 +295,6 @@ pub const Node = extern union {
294295 .int_to_ptr,
295296 .array_cat,
296297 .ellipsis3,
297 .switch_prong,
298298 .assign,
299299 .align_cast,
300300 .array_access,
......@@ -312,8 +312,7 @@ pub const Node = extern union {
312312 => Payload.Value,
313313 .@"if" => Payload.If,
314314 .@"while" => Payload.While,
315 .@"switch", .array_init => Payload.Switch,
316 .@"break" => Payload.Break,
315 .@"switch", .array_init,.switch_prong => Payload.Switch,
317316 .break_val => Payload.BreakVal,
318317 .call => Payload.Call,
319318 .var_decl => Payload.VarDecl,
......@@ -377,6 +376,37 @@ pub const Node = extern union {
377376 std.debug.assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
378377 return .{ .ptr_otherwise = payload };
379378 }
379
380 pub fn isNoreturn(node: Node, break_counts: bool) bool {
381 switch (node.tag()) {
382 .block => {
383 const block_node = node.castTag(.block).?;
384 if (block_node.data.stmts.len == 0) return false;
385
386 const last = block_node.data.stmts[block_node.data.stmts.len - 1];
387 return last.isNoreturn(break_counts);
388 },
389 .@"switch" => {
390 const switch_node = node.castTag(.@"switch").?;
391
392 for (switch_node.data.cases) |case| {
393 const body = if (case.castTag(.switch_else)) |some|
394 some.data
395 else if (case.castTag(.switch_prong)) |some|
396 some.data.cond
397 else unreachable;
398
399 if (!body.isNoreturn(break_counts)) return false;
400 }
401 return true;
402 },
403 .@"return", .return_void => return true,
404 .break_val, .@"break" => if (break_counts) return true,
405 else => {},
406 }
407 return false;
408 }
409
380410};
381411
382412pub const Payload = struct {
......@@ -434,11 +464,6 @@ pub const Payload = struct {
434464 },
435465 };
436466
437 pub const Break = struct {
438 base: Payload,
439 data: ?[]const u8,
440 };
441
442467 pub const BreakVal = struct {
443468 base: Payload,
444469 data: struct {
......@@ -855,22 +880,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
855880 .rhs = undefined,
856881 },
857882 }),
858 .@"break" => {
859 const payload = node.castTag(.@"break").?.data;
860 const tok = try c.addToken(.keyword_break, "break");
861 const break_label = if (payload) |some| blk: {
862 _ = try c.addToken(.colon, ":");
863 break :blk try c.addIdentifier(some);
864 } else 0;
865 return c.addNode(.{
866 .tag = .@"break",
867 .main_token = tok,
868 .data = .{
869 .lhs = break_label,
870 .rhs = 0,
871 },
872 });
873 },
883 .@"break" => return c.addNode(.{
884 .tag = .@"break",
885 .main_token = try c.addToken(.keyword_break, "break"),
886 .data = .{
887 .lhs = 0,
888 .rhs = 0,
889 },
890 }),
874891 .break_val => {
875892 const payload = node.castTag(.break_val).?.data;
876893 const tok = try c.addToken(.keyword_break, "break");
......@@ -1447,15 +1464,37 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
14471464 },
14481465 .switch_prong => {
14491466 const payload = node.castTag(.switch_prong).?.data;
1450 const item = try renderNode(c, payload.lhs);
1451 return c.addNode(.{
1452 .tag = .switch_case_one,
1453 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1454 .data = .{
1455 .lhs = item,
1456 .rhs = try renderNode(c, payload.rhs),
1457 },
1458 });
1467 var items = try c.gpa.alloc(NodeIndex, std.math.max(payload.cases.len, 1));
1468 defer c.gpa.free(items);
1469 items[0] = 0;
1470 for (payload.cases) |item, i| {
1471 if (i != 0) _ = try c.addToken(.comma, ",");
1472 items[i] = try renderNode(c, item);
1473 }
1474 _ = try c.addToken(.r_brace, "}");
1475 if (items.len < 2) {
1476 return c.addNode(.{
1477 .tag = .switch_case_one,
1478 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1479 .data = .{
1480 .lhs = items[0],
1481 .rhs = try renderNode(c, payload.cond),
1482 },
1483 });
1484 } else {
1485 const span = try c.listToSpan(items);
1486 return c.addNode(.{
1487 .tag = .switch_case,
1488 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1489 .data = .{
1490 .lhs = try c.addExtra(NodeSubRange{
1491 .start = span.start,
1492 .end = span.end,
1493 }),
1494 .rhs = try renderNode(c, payload.cond),
1495 },
1496 });
1497 }
14591498 },
14601499 .opaque_literal => {
14611500 const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
......@@ -1870,7 +1909,7 @@ fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
18701909
18711910fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
18721911 switch (node.tag()) {
1873 .block, .empty_block, .block_single, => {},
1912 .block, .empty_block, .block_single => {},
18741913 .@"if" => {
18751914 const payload = node.castTag(.@"if").?.data;
18761915 if (payload.@"else") |some|
test/translate_c.zig+36-40
......@@ -276,27 +276,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
276276 \\ }
277277 \\}
278278 , &[_][]const u8{ // TODO properly translate this
279 \\pub export fn main() c_int {
280 \\ var i: c_int = 2;
281 \\ @"switch": {
282 \\ case_1: {
283 \\ case: {
284 \\ switch (i) {
285 \\ @as(c_int, 0) => break :case,
286 \\ @as(c_int, 2) => break :case_1,
287 \\ else => break :@"switch",
288 \\ }
289 \\ }
290 \\ }
291 \\ {
292 \\ {
293 \\ i += @as(c_int, 2);
294 \\ }
295 \\ i += @as(c_int, 1);
296 \\ }
297 \\ }
298 \\ return 0;
299 \\}
279 \\source.h:5:13: warning: TODO complex switch
280 ,
281 \\source.h:1:5: warning: unable to translate function, demoted to extern
282 \\pub extern fn main() c_int;
300283 });
301284
302285 cases.add("correct semicolon after infixop",
......@@ -2013,34 +1996,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20131996 \\ default:
20141997 \\ res = 3 * i;
20151998 \\ break;
1999 \\ break;
20162000 \\ case 4:
2001 \\ case 5:
2002 \\ res = 69;
2003 \\ {
20172004 \\ res = 5;
2005 \\ return;
2006 \\ }
2007 \\ case 6:
2008 \\ res = 1;
2009 \\ return;
20182010 \\ }
20192011 \\}
20202012 , &[_][]const u8{
20212013 \\pub export fn switch_fn(arg_i: c_int) void {
20222014 \\ var i = arg_i;
20232015 \\ var res: c_int = 0;
2024 \\ @"switch": {
2025 \\ case_2: {
2026 \\ default: {
2027 \\ case_1: {
2028 \\ case: {
2029 \\ switch (i) {
2030 \\ @as(c_int, 0) => break :case,
2031 \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1,
2032 \\ else => break :default,
2033 \\ @as(c_int, 4) => break :case_2,
2034 \\ }
2035 \\ }
2036 \\ res = 1;
2037 \\ }
2038 \\ res = 2;
2039 \\ }
2016 \\ switch (i) {
2017 \\ @as(c_int, 0) => {
2018 \\ res = 1;
2019 \\ res = 2;
20402020 \\ res = @as(c_int, 3) * i;
2041 \\ break :@"switch";
2042 \\ }
2043 \\ res = 5;
2021 \\ },
2022 \\ @as(c_int, 1)...@as(c_int, 3) => {
2023 \\ res = 2;
2024 \\ res = @as(c_int, 3) * i;
2025 \\ },
2026 \\ else => {
2027 \\ res = @as(c_int, 3) * i;
2028 \\ },
2029 \\ @as(c_int, 4), @as(c_int, 5) => {
2030 \\ res = 69;
2031 \\ {
2032 \\ res = 5;
2033 \\ return;
2034 \\ }
2035 \\ },
2036 \\ @as(c_int, 6) => {
2037 \\ res = 1;
2038 \\ return;
2039 \\ },
20442040 \\ }
20452041 \\}
20462042 });