| ... | @@ -31,7 +31,6 @@ const Scope = struct { | ... | @@ -31,7 +31,6 @@ const Scope = struct { |
| 31 | parent: ?*Scope, | 31 | parent: ?*Scope, |
| 32 | | 32 | |
| 33 | const Id = enum { | 33 | const Id = enum { |
| 34 | @"switch", | | |
| 35 | block, | 34 | block, |
| 36 | root, | 35 | root, |
| 37 | condition, | 36 | condition, |
| ... | @@ -39,17 +38,6 @@ const Scope = struct { | ... | @@ -39,17 +38,6 @@ const Scope = struct { |
| 39 | do_loop, | 38 | do_loop, |
| 40 | }; | 39 | }; |
| 41 | | 40 | |
| 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 | | | |
| 53 | /// Used for the scope of condition expressions, for example `if (cond)`. | 41 | /// Used for the scope of condition expressions, for example `if (cond)`. |
| 54 | /// The block is lazily initialised because it is only needed for rare | 42 | /// The block is lazily initialised because it is only needed for rare |
| 55 | /// cases of comma operators being used. | 43 | /// cases of comma operators being used. |
| ... | @@ -230,7 +218,7 @@ const Scope = struct { | ... | @@ -230,7 +218,7 @@ const Scope = struct { |
| 230 | return switch (scope.id) { | 218 | return switch (scope.id) { |
| 231 | .root => return name, | 219 | .root => return name, |
| 232 | .block => @fieldParentPtr(Block, "base", scope).getAlias(name), | 220 | .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), |
| 234 | }; | 222 | }; |
| 235 | } | 223 | } |
| 236 | | 224 | |
| ... | @@ -238,7 +226,7 @@ const Scope = struct { | ... | @@ -238,7 +226,7 @@ const Scope = struct { |
| 238 | return switch (scope.id) { | 226 | return switch (scope.id) { |
| 239 | .root => @fieldParentPtr(Root, "base", scope).contains(name), | 227 | .root => @fieldParentPtr(Root, "base", scope).contains(name), |
| 240 | .block => @fieldParentPtr(Block, "base", scope).contains(name), | 228 | .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), |
| 242 | }; | 230 | }; |
| 243 | } | 231 | } |
| 244 | | 232 | |
| ... | @@ -247,24 +235,12 @@ const Scope = struct { | ... | @@ -247,24 +235,12 @@ const Scope = struct { |
| 247 | while (true) { | 235 | while (true) { |
| 248 | switch (scope.id) { | 236 | switch (scope.id) { |
| 249 | .root => unreachable, | 237 | .root => unreachable, |
| 250 | .@"switch" => return scope, | | |
| 251 | .loop, .do_loop => return scope, | 238 | .loop, .do_loop => return scope, |
| 252 | else => scope = scope.parent.?, | 239 | else => scope = scope.parent.?, |
| 253 | } | 240 | } |
| 254 | } | 241 | } |
| 255 | } | 242 | } |
| 256 | | 243 | |
| 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 | | | |
| 268 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. | 244 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. |
| 269 | fn appendNode(inner: *Scope, node: Node) !void { | 245 | fn appendNode(inner: *Scope, node: Node) !void { |
| 270 | var scope = inner; | 246 | var scope = inner; |
| ... | @@ -570,7 +546,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -570,7 +546,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 570 | } | 546 | } |
| 571 | | 547 | |
| 572 | const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt); | 548 | 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) { |
| 574 | error.OutOfMemory => |e| return e, | 550 | error.OutOfMemory => |e| return e, |
| 575 | error.UnsupportedTranslation, | 551 | error.UnsupportedTranslation, |
| 576 | error.UnsupportedType, | 552 | error.UnsupportedType, |
| ... | @@ -583,24 +559,10 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -583,24 +559,10 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 583 | }; | 559 | }; |
| 584 | // add return statement if the function didn't have one | 560 | // add return statement if the function didn't have one |
| 585 | blk: { | 561 | blk: { |
| 586 | if (fn_ty.getNoReturnAttr()) break :blk; | 562 | const maybe_body = try block_scope.complete(c); |
| 587 | if (isCVoid(return_qt)) break :blk; | 563 | if (fn_ty.getNoReturnAttr() or isCVoid(return_qt) or maybe_body.isNoreturn(false)) { |
| 588 | | 564 | proto_node.data.body = maybe_body; |
| 589 | if (block_scope.statements.items.len > 0) { | 565 | break :blk; |
| 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 | } | | |
| 604 | } | 566 | } |
| 605 | | 567 | |
| 606 | const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { | 568 | 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 { | ... | @@ -616,9 +578,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 616 | }; | 578 | }; |
| 617 | const ret = try Tag.@"return".create(c.arena, rhs); | 579 | const ret = try Tag.@"return".create(c.arena, rhs); |
| 618 | try block_scope.statements.append(ret); | 580 | try block_scope.statements.append(ret); |
| | 581 | proto_node.data.body = try block_scope.complete(c); |
| 619 | } | 582 | } |
| 620 | | 583 | |
| 621 | proto_node.data.body = try block_scope.complete(c); | | |
| 622 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); | 584 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 623 | } | 585 | } |
| 624 | | 586 | |
| ... | @@ -1079,7 +1041,7 @@ fn transStmt( | ... | @@ -1079,7 +1041,7 @@ fn transStmt( |
| 1079 | return Tag.empty_block.init(); | 1041 | return Tag.empty_block.init(); |
| 1080 | }, | 1042 | }, |
| 1081 | .ContinueStmtClass => return Tag.@"continue".init(), | 1043 | .ContinueStmtClass => return Tag.@"continue".init(), |
| 1082 | .BreakStmtClass => return transBreak(c, scope), | 1044 | .BreakStmtClass => return Tag.@"break".init(), |
| 1083 | .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), | 1045 | .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), |
| 1084 | .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), | 1046 | .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), |
| 1085 | .ConditionalOperatorClass => { | 1047 | .ConditionalOperatorClass => { |
| ... | @@ -1089,8 +1051,9 @@ fn transStmt( | ... | @@ -1089,8 +1051,9 @@ fn transStmt( |
| 1089 | return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); | 1051 | return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); |
| 1090 | }, | 1052 | }, |
| 1091 | .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)), | 1053 | .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)), |
| 1092 | .CaseStmtClass => return transCase(c, scope, @ptrCast(*const clang.CaseStmt, stmt)), | 1054 | .CaseStmtClass, .DefaultStmtClass => { |
| 1093 | .DefaultStmtClass => return transDefault(c, scope, @ptrCast(*const clang.DefaultStmt, stmt)), | 1055 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{}); |
| | 1056 | }, |
| 1094 | .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), | 1057 | .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), |
| 1095 | .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), | 1058 | .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), |
| 1096 | .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), | 1059 | .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), |
| ... | @@ -1107,13 +1070,7 @@ fn transStmt( | ... | @@ -1107,13 +1070,7 @@ fn transStmt( |
| 1107 | return maybeSuppressResult(c, scope, result_used, expr); | 1070 | return maybeSuppressResult(c, scope, result_used, expr); |
| 1108 | }, | 1071 | }, |
| 1109 | else => { | 1072 | else => { |
| 1110 | return fail( | 1073 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); |
| 1111 | c, | | |
| 1112 | error.UnsupportedTranslation, | | |
| 1113 | stmt.getBeginLoc(), | | |
| 1114 | "TODO implement translation of stmt class {s}", | | |
| 1115 | .{@tagName(sc)}, | | |
| 1116 | ); | | |
| 1117 | }, | 1074 | }, |
| 1118 | } | 1075 | } |
| 1119 | } | 1076 | } |
| ... | @@ -1255,14 +1212,13 @@ fn transBinaryOperator( | ... | @@ -1255,14 +1212,13 @@ fn transBinaryOperator( |
| 1255 | | 1212 | |
| 1256 | fn transCompoundStmtInline( | 1213 | fn transCompoundStmtInline( |
| 1257 | c: *Context, | 1214 | c: *Context, |
| 1258 | parent_scope: *Scope, | | |
| 1259 | stmt: *const clang.CompoundStmt, | 1215 | stmt: *const clang.CompoundStmt, |
| 1260 | block: *Scope.Block, | 1216 | block: *Scope.Block, |
| 1261 | ) TransError!void { | 1217 | ) TransError!void { |
| 1262 | var it = stmt.body_begin(); | 1218 | var it = stmt.body_begin(); |
| 1263 | const end_it = stmt.body_end(); | 1219 | const end_it = stmt.body_end(); |
| 1264 | while (it != end_it) : (it += 1) { | 1220 | 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); |
| 1266 | if (result.tag() == .declaration) continue; | 1222 | if (result.tag() == .declaration) continue; |
| 1267 | try block.statements.append(result); | 1223 | try block.statements.append(result); |
| 1268 | } | 1224 | } |
| ... | @@ -1271,7 +1227,7 @@ fn transCompoundStmtInline( | ... | @@ -1271,7 +1227,7 @@ fn transCompoundStmtInline( |
| 1271 | fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { | 1227 | fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { |
| 1272 | var block_scope = try Scope.Block.init(c, scope, false); | 1228 | var block_scope = try Scope.Block.init(c, scope, false); |
| 1273 | defer block_scope.deinit(); | 1229 | defer block_scope.deinit(); |
| 1274 | try transCompoundStmtInline(c, &block_scope.base, stmt, &block_scope); | 1230 | try transCompoundStmtInline(c, stmt, &block_scope); |
| 1275 | return try block_scope.complete(c); | 1231 | return try block_scope.complete(c); |
| 1276 | } | 1232 | } |
| 1277 | | 1233 | |
| ... | @@ -2162,7 +2118,7 @@ fn transDoWhileLoop( | ... | @@ -2162,7 +2118,7 @@ fn transDoWhileLoop( |
| 2162 | defer cond_scope.deinit(); | 2118 | defer cond_scope.deinit(); |
| 2163 | const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); | 2119 | const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); |
| 2164 | const if_not_break = switch (cond.tag()) { | 2120 | const if_not_break = switch (cond.tag()) { |
| 2165 | .false_literal => try Tag.@"break".create(c.arena, null), | 2121 | .false_literal => Tag.@"break".init(), |
| 2166 | .true_literal => { | 2122 | .true_literal => { |
| 2167 | const body_node = try transStmt(c, scope, stmt.getBody(), .unused); | 2123 | const body_node = try transStmt(c, scope, stmt.getBody(), .unused); |
| 2168 | return Tag.while_true.create(c.arena, body_node); | 2124 | return Tag.while_true.create(c.arena, body_node); |
| ... | @@ -2263,133 +2219,189 @@ fn transSwitch( | ... | @@ -2263,133 +2219,189 @@ fn transSwitch( |
| 2263 | }; | 2219 | }; |
| 2264 | defer cond_scope.deinit(); | 2220 | defer cond_scope.deinit(); |
| 2265 | const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used); | 2221 | 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(); | | |
| 2286 | | 2222 | |
| 2287 | // tmp block that all statements will go before being picked up by a case or default | 2223 | var cases = std.ArrayList(Node).init(c.gpa); |
| 2288 | var block_scope = try Scope.Block.init(c, &switch_scope.base, false); | 2224 | defer cases.deinit(); |
| 2289 | defer block_scope.deinit(); | 2225 | var has_default = false; |
| 2290 | | 2226 | |
| 2291 | // Note that we do not defer a deinit here; the switch_scope.pending_block field | 2227 | const body = stmt.getBody(); |
| 2292 | // has its own memory management. This resource is freed inside `transCase` and | 2228 | assert(body.getStmtClass() == .CompoundStmtClass); |
| 2293 | // then the final pending_block is freed at the bottom of this function with | 2229 | const compound_stmt = @ptrCast(*const clang.CompoundStmt, body); |
| 2294 | // pending_block.deinit(). | 2230 | var it = compound_stmt.body_begin(); |
| 2295 | switch_scope.pending_block = try Scope.Block.init(c, scope, false); | 2231 | const end_it = compound_stmt.body_end(); |
| 2296 | try switch_scope.pending_block.statements.append(Node.initPayload(&switch_node.base)); | 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 | }; |
| 2297 | | 2264 | |
| 2298 | const last = try transStmt(c, &block_scope.base, stmt.getBody(), .unused); | 2265 | const res = try transSwitchProngStmt(c, scope, sub, it, end_it); |
| 2299 | | 2266 | |
| 2300 | // take all pending statements | 2267 | const switch_else = try Tag.switch_else.create(c.arena, res); |
| 2301 | const last_block_stmts = last.castTag(.block).?.data.stmts; | 2268 | try cases.append(switch_else); |
| 2302 | try switch_scope.pending_block.statements.ensureCapacity( | 2269 | }, |
| 2303 | switch_scope.pending_block.statements.items.len + last_block_stmts.len, | 2270 | else => {}, // collected in transSwitchProngStmt |
| 2304 | ); | 2271 | } |
| 2305 | for (last_block_stmts) |n| { | | |
| 2306 | switch_scope.pending_block.statements.appendAssumeCapacity(n); | | |
| 2307 | } | 2272 | } |
| 2308 | | 2273 | |
| 2309 | if (switch_scope.default_label == null) { | 2274 | if (!has_default) { |
| 2310 | switch_scope.switch_label = try block_scope.makeMangledName(c, "switch"); | 2275 | const else_prong = try Tag.switch_else.create(c.arena, Tag.@"break".init()); |
| 2311 | } | 2276 | try cases.append(else_prong); |
| 2312 | if (switch_scope.switch_label) |l| { | | |
| 2313 | switch_scope.pending_block.label = l; | | |
| 2314 | } | 2277 | } |
| 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 | | | |
| 2329 | fn 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); | | |
| 2345 | | 2278 | |
| 2346 | const switch_prong = try Tag.switch_prong.create(c.arena, .{ | 2279 | return Tag.@"switch".create(c.arena, .{ |
| 2347 | .lhs = expr, | 2280 | .cond = switch_expr, |
| 2348 | .rhs = try Tag.@"break".create(c.arena, label), | 2281 | .cases = try c.arena.dupe(Node, cases.items), |
| 2349 | }); | 2282 | }); |
| 2350 | try switch_scope.cases.append(switch_prong); | 2283 | } |
| 2351 | | 2284 | |
| 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. |
| | 2287 | fn 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); |
| 2353 | | 2300 | |
| 2354 | // take all pending statements | 2301 | if (seen_default) { |
| 2355 | try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); | 2302 | items.items.len = 0; |
| 2356 | block_scope.statements.shrinkAndFree(0); | 2303 | sub = case_stmt.getSubStmt(); |
| | 2304 | continue; |
| | 2305 | } |
| 2357 | | 2306 | |
| 2358 | const pending_node = try switch_scope.pending_block.complete(c); | 2307 | const expr = if (case_stmt.getRHS()) |rhs| blk: { |
| 2359 | switch_scope.pending_block.deinit(); | 2308 | const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used); |
| 2360 | switch_scope.pending_block = try Scope.Block.init(c, scope, false); | 2309 | const rhs_node = try transExprCoercing(c, scope, rhs, .used); |
| 2361 | | 2310 | |
| 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); |
| 2363 | | 2314 | |
| 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 | } |
| 2365 | } | 2321 | } |
| 2366 | | 2322 | |
| 2367 | fn 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. |
| | 2325 | fn transSwitchProngStmt( |
| 2368 | c: *Context, | 2326 | c: *Context, |
| 2369 | scope: *Scope, | 2327 | 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, |
| 2371 | ) TransError!Node { | 2331 | ) TransError!Node { |
| 2372 | const block_scope = try scope.findBlockScope(c); | 2332 | switch (stmt.getStmtClass()) { |
| 2373 | const switch_scope = scope.getSwitch(); | 2333 | .BreakStmtClass => return Tag.empty_block.init(), |
| 2374 | switch_scope.default_label = try block_scope.makeMangledName(c, "default"); | 2334 | .ReturnStmtClass => return transStmt(c, scope, stmt, .unused), |
| 2375 | | 2335 | .CaseStmtClass, .DefaultStmtClass => unreachable, |
| 2376 | const else_prong = try Tag.switch_else.create( | 2336 | else => { |
| 2377 | c.arena, | 2337 | var block_scope = try Scope.Block.init(c, scope, false); |
| 2378 | try Tag.@"break".create(c.arena, switch_scope.default_label.?), | 2338 | defer block_scope.deinit(); |
| 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); | | |
| 2386 | | 2339 | |
| 2387 | const pending_node = try switch_scope.pending_block.complete(c); | 2340 | // we do not need to translate `stmt` since it is the first stmt of `parent_it` |
| 2388 | switch_scope.pending_block.deinit(); | 2341 | try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it); |
| 2389 | switch_scope.pending_block = try Scope.Block.init(c, scope, false); | 2342 | return try block_scope.complete(c); |
| 2390 | try switch_scope.pending_block.statements.append(pending_node); | 2343 | }, |
| | 2344 | } |
| | 2345 | } |
| 2391 | | 2346 | |
| 2392 | return transStmt(c, scope, stmt.getSubStmt(), .unused); | 2347 | /// Collects all statements seen by this case into a block. |
| | 2348 | fn 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; |
| 2393 | } | 2405 | } |
| 2394 | | 2406 | |
| 2395 | fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { | 2407 | fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { |
| ... | @@ -3025,19 +3037,6 @@ fn transCPtrCast( | ... | @@ -3025,19 +3037,6 @@ fn transCPtrCast( |
| 3025 | } | 3037 | } |
| 3026 | } | 3038 | } |
| 3027 | | 3039 | |
| 3028 | fn 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 | | | |
| 3041 | fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { | 3040 | fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { |
| 3042 | // TODO use something more accurate | 3041 | // TODO use something more accurate |
| 3043 | var dbl = stmt.getValueAsApproximateDouble(); | 3042 | var dbl = stmt.getValueAsApproximateDouble(); |