| ... | ... | @@ -31,7 +31,6 @@ const Scope = struct { |
| 31 | 31 | parent: ?*Scope, |
| 32 | 32 | |
| 33 | 33 | const Id = enum { |
| 34 | | @"switch", |
| 35 | 34 | block, |
| 36 | 35 | root, |
| 37 | 36 | condition, |
| ... | ... | @@ -39,17 +38,6 @@ const Scope = struct { |
| 39 | 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 | 41 | /// Used for the scope of condition expressions, for example `if (cond)`. |
| 54 | 42 | /// The block is lazily initialised because it is only needed for rare |
| 55 | 43 | /// cases of comma operators being used. |
| ... | ... | @@ -230,7 +218,7 @@ const Scope = struct { |
| 230 | 218 | return switch (scope.id) { |
| 231 | 219 | .root => return name, |
| 232 | 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 | 226 | return switch (scope.id) { |
| 239 | 227 | .root => @fieldParentPtr(Root, "base", scope).contains(name), |
| 240 | 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 | 235 | while (true) { |
| 248 | 236 | switch (scope.id) { |
| 249 | 237 | .root => unreachable, |
| 250 | | .@"switch" => return scope, |
| 251 | 238 | .loop, .do_loop => return scope, |
| 252 | 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 | 244 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. |
| 269 | 245 | fn appendNode(inner: *Scope, node: Node) !void { |
| 270 | 246 | var scope = inner; |
| ... | ... | @@ -570,7 +546,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 570 | 546 | } |
| 571 | 547 | |
| 572 | 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 | 550 | error.OutOfMemory => |e| return e, |
| 575 | 551 | error.UnsupportedTranslation, |
| 576 | 552 | error.UnsupportedType, |
| ... | ... | @@ -583,24 +559,10 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 583 | 559 | }; |
| 584 | 560 | // add return statement if the function didn't have one |
| 585 | 561 | 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; |
| 604 | 566 | } |
| 605 | 567 | |
| 606 | 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 | 578 | }; |
| 617 | 579 | const ret = try Tag.@"return".create(c.arena, rhs); |
| 618 | 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 | 584 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 623 | 585 | } |
| 624 | 586 | |
| ... | ... | @@ -1079,7 +1041,7 @@ fn transStmt( |
| 1079 | 1041 | return Tag.empty_block.init(); |
| 1080 | 1042 | }, |
| 1081 | 1043 | .ContinueStmtClass => return Tag.@"continue".init(), |
| 1082 | | .BreakStmtClass => return transBreak(c, scope), |
| 1044 | .BreakStmtClass => return Tag.@"break".init(), |
| 1083 | 1045 | .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), |
| 1084 | 1046 | .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), |
| 1085 | 1047 | .ConditionalOperatorClass => { |
| ... | ... | @@ -1089,8 +1051,9 @@ fn transStmt( |
| 1089 | 1051 | return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); |
| 1090 | 1052 | }, |
| 1091 | 1053 | .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 | }, |
| 1094 | 1057 | .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), |
| 1095 | 1058 | .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), |
| 1096 | 1059 | .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), |
| ... | ... | @@ -1107,13 +1070,7 @@ fn transStmt( |
| 1107 | 1070 | return maybeSuppressResult(c, scope, result_used, expr); |
| 1108 | 1071 | }, |
| 1109 | 1072 | 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)}); |
| 1117 | 1074 | }, |
| 1118 | 1075 | } |
| 1119 | 1076 | } |
| ... | ... | @@ -1255,14 +1212,13 @@ fn transBinaryOperator( |
| 1255 | 1212 | |
| 1256 | 1213 | fn transCompoundStmtInline( |
| 1257 | 1214 | c: *Context, |
| 1258 | | parent_scope: *Scope, |
| 1259 | 1215 | stmt: *const clang.CompoundStmt, |
| 1260 | 1216 | block: *Scope.Block, |
| 1261 | 1217 | ) TransError!void { |
| 1262 | 1218 | var it = stmt.body_begin(); |
| 1263 | 1219 | const end_it = stmt.body_end(); |
| 1264 | 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 | 1222 | if (result.tag() == .declaration) continue; |
| 1267 | 1223 | try block.statements.append(result); |
| 1268 | 1224 | } |
| ... | ... | @@ -1271,7 +1227,7 @@ fn transCompoundStmtInline( |
| 1271 | 1227 | fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { |
| 1272 | 1228 | var block_scope = try Scope.Block.init(c, scope, false); |
| 1273 | 1229 | defer block_scope.deinit(); |
| 1274 | | try transCompoundStmtInline(c, &block_scope.base, stmt, &block_scope); |
| 1230 | try transCompoundStmtInline(c, stmt, &block_scope); |
| 1275 | 1231 | return try block_scope.complete(c); |
| 1276 | 1232 | } |
| 1277 | 1233 | |
| ... | ... | @@ -2162,7 +2118,7 @@ fn transDoWhileLoop( |
| 2162 | 2118 | defer cond_scope.deinit(); |
| 2163 | 2119 | const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); |
| 2164 | 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 | 2122 | .true_literal => { |
| 2167 | 2123 | const body_node = try transStmt(c, scope, stmt.getBody(), .unused); |
| 2168 | 2124 | return Tag.while_true.create(c.arena, body_node); |
| ... | ... | @@ -2263,133 +2219,189 @@ fn transSwitch( |
| 2263 | 2219 | }; |
| 2264 | 2220 | defer cond_scope.deinit(); |
| 2265 | 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 |
| 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 | }; |
| 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 |
| 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 | } |
| 2307 | 2272 | } |
| 2308 | 2273 | |
| 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); |
| 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, .{ |
| 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), |
| 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 |
| 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 | } |
| 2357 | 2306 | |
| 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); |
| 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 | 2326 | c: *Context, |
| 2369 | 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 | 2331 | ) 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(); |
| 2386 | 2339 | |
| 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 | } |
| 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 | 2407 | fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { |
| ... | ... | @@ -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 | 3040 | fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { |
| 3042 | 3041 | // TODO use something more accurate |
| 3043 | 3042 | var dbl = stmt.getValueAsApproximateDouble(); |