authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-12-26 02:17:36+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-12-26 02:26:53+02:00
log40aad4f47e1ab02a1ff6109f4b6f06af00d1f503
tree4295d78569af3bbb2a3417bf81dd6fc47be58024
parentc99c6c0a68bb1c32123a027c274b165b3891e88b
signaturelock-open Commit is signed but in an unrecognized format.

stage2: break and continue out of loops


3 files changed, 201 insertions(+), 44 deletions(-)

src/Module.zig+4
...@@ -796,6 +796,10 @@ pub const Scope = struct {...@@ -796,6 +796,10 @@ pub const Scope = struct {
796 /// The first N instructions in a function body ZIR are arg instructions.796 /// The first N instructions in a function body ZIR are arg instructions.
797 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},797 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
798 label: ?Label = null,798 label: ?Label = null,
799 break_block: ?*zir.Inst.Block = null,
800 continue_block: ?*zir.Inst.Block = null,
801 /// only valid if label != null or (continue_block and break_block) != null
802 break_result_loc: astgen.ResultLoc = undefined,
799803
800 pub const Label = struct {804 pub const Label = struct {
801 token: ast.TokenIndex,805 token: ast.TokenIndex,
src/astgen.zig+105-44
...@@ -261,6 +261,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -261,6 +261,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
261 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),261 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
262 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),262 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
263 .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),263 .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
264 .Continue => return rlWrap(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)),
264 .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),265 .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
265 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),266 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
266 .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),267 .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
...@@ -291,7 +292,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -291,7 +292,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
291 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),292 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
292 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),293 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
293 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),294 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
294 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
295 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),295 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
296 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),296 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
297 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),297 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
...@@ -339,48 +339,97 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr...@@ -339,48 +339,97 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
339 const tree = parent_scope.tree();339 const tree = parent_scope.tree();
340 const src = tree.token_locs[node.ltoken].start;340 const src = tree.token_locs[node.ltoken].start;
341341
342 if (node.getLabel()) |break_label| {342 // Look for the label in the scope.
343 // Look for the label in the scope.343 var scope = parent_scope;
344 var scope = parent_scope;344 while (true) {
345 while (true) {345 switch (scope.tag) {
346 switch (scope.tag) {346 .gen_zir => {
347 .gen_zir => {347 const gen_zir = scope.cast(Scope.GenZIR).?;
348 const gen_zir = scope.cast(Scope.GenZIR).?;348
349 const block_inst = blk: {
350 if (node.getLabel()) |break_label| {
351 if (gen_zir.label) |label| {
352 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
353 break :blk label.block_inst;
354 }
355 }
356 } else if (gen_zir.break_block) |inst| {
357 break :blk inst;
358 }
359 scope = gen_zir.parent;
360 continue;
361 };
362
363 if (node.getRHS()) |rhs| {
364 // Most result location types can be forwarded directly; however
365 // if we need to write to a pointer which has an inferred type,
366 // proper type inference requires peer type resolution on the block's
367 // break operand expressions.
368 const branch_rl: ResultLoc = switch (gen_zir.break_result_loc) {
369 .discard, .none, .ty, .ptr, .ref => gen_zir.break_result_loc,
370 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst },
371 };
372 const operand = try expr(mod, parent_scope, branch_rl, rhs);
373 return try addZIRInst(mod, parent_scope, src, zir.Inst.Break, .{
374 .block = block_inst,
375 .operand = operand,
376 }, .{});
377 } else {
378 return try addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{
379 .block = block_inst,
380 }, .{});
381 }
382 },
383 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
384 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
385 else => if (node.getLabel()) |break_label| {
386 const label_name = try identifierTokenString(mod, parent_scope, break_label);
387 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
388 } else {
389 return mod.failTok(parent_scope, src, "break expression outside loop", .{});
390 },
391 }
392 }
393}
394
395fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
396 const tree = parent_scope.tree();
397 const src = tree.token_locs[node.ltoken].start;
398
399 // Look for the label in the scope.
400 var scope = parent_scope;
401 while (true) {
402 switch (scope.tag) {
403 .gen_zir => {
404 const gen_zir = scope.cast(Scope.GenZIR).?;
405 const continue_block = gen_zir.continue_block orelse {
406 scope = gen_zir.parent;
407 continue;
408 };
409 if (node.getLabel()) |break_label| blk: {
349 if (gen_zir.label) |label| {410 if (gen_zir.label) |label| {
350 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {411 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
351 if (node.getRHS()) |rhs| {412 break :blk;
352 // Most result location types can be forwarded directly; however
353 // if we need to write to a pointer which has an inferred type,
354 // proper type inference requires peer type resolution on the block's
355 // break operand expressions.
356 const branch_rl: ResultLoc = switch (label.result_loc) {
357 .discard, .none, .ty, .ptr, .ref => label.result_loc,
358 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
359 };
360 const operand = try expr(mod, parent_scope, branch_rl, rhs);
361 return try addZIRInst(mod, scope, src, zir.Inst.Break, .{
362 .block = label.block_inst,
363 .operand = operand,
364 }, .{});
365 } else {
366 return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{
367 .block = label.block_inst,
368 }, .{});
369 }
370 }413 }
371 }414 }
415 // found continue but either it has a different label, or no label
372 scope = gen_zir.parent;416 scope = gen_zir.parent;
373 },417 continue;
374 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,418 }
375 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,419
376 else => {420 return addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{
377 const label_name = try identifierTokenString(mod, parent_scope, break_label);421 .block = continue_block,
378 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});422 }, .{});
379 },423 },
380 }424 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
425 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
426 else => if (node.getLabel()) |break_label| {
427 const label_name = try identifierTokenString(mod, parent_scope, break_label);
428 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
429 } else {
430 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
431 },
381 }432 }
382 } else {
383 return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
384 }433 }
385}434}
386435
...@@ -426,11 +475,11 @@ fn labeledBlockExpr(...@@ -426,11 +475,11 @@ fn labeledBlockExpr(
426 .decl = parent_scope.decl().?,475 .decl = parent_scope.decl().?,
427 .arena = gen_zir.arena,476 .arena = gen_zir.arena,
428 .instructions = .{},477 .instructions = .{},
478 .break_result_loc = rl,
429 // TODO @as here is working around a stage1 miscompilation bug :(479 // TODO @as here is working around a stage1 miscompilation bug :(
430 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{480 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
431 .token = block_node.label,481 .token = block_node.label,
432 .block_inst = block_inst,482 .block_inst = block_inst,
433 .result_loc = rl,
434 }),483 }),
435 };484 };
436 defer block_scope.instructions.deinit(mod.gpa);485 defer block_scope.instructions.deinit(mod.gpa);
...@@ -1289,9 +1338,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1289,9 +1338,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1289 }1338 }
1290 }1339 }
12911340
1292 if (while_node.label) |tok|
1293 return mod.failTok(scope, tok, "TODO labeled while", .{});
1294
1295 if (while_node.inline_token) |tok|1341 if (while_node.inline_token) |tok|
1296 return mod.failTok(scope, tok, "TODO inline while", .{});1342 return mod.failTok(scope, tok, "TODO inline while", .{});
12971343
...@@ -1308,6 +1354,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1308,6 +1354,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1308 .decl = expr_scope.decl,1354 .decl = expr_scope.decl,
1309 .arena = expr_scope.arena,1355 .arena = expr_scope.arena,
1310 .instructions = .{},1356 .instructions = .{},
1357 .break_result_loc = rl,
1311 };1358 };
1312 defer loop_scope.instructions.deinit(mod.gpa);1359 defer loop_scope.instructions.deinit(mod.gpa);
13131360
...@@ -1348,6 +1395,14 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1348,6 +1395,14 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1348 const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{1395 const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
1349 .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),1396 .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
1350 });1397 });
1398 loop_scope.break_block = while_block;
1399 loop_scope.continue_block = cond_block;
1400 if (while_node.label) |some| {
1401 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
1402 .token = some,
1403 .block_inst = while_block,
1404 });
1405 }
13511406
1352 const then_src = tree.token_locs[while_node.body.lastToken()].start;1407 const then_src = tree.token_locs[while_node.body.lastToken()].start;
1353 var then_scope: Scope.GenZIR = .{1408 var then_scope: Scope.GenZIR = .{
...@@ -1414,9 +1469,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1414,9 +1469,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1414}1469}
14151470
1416fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {1471fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
1417 if (for_node.label) |tok|
1418 return mod.failTok(scope, tok, "TODO labeled for", .{});
1419
1420 if (for_node.inline_token) |tok|1472 if (for_node.inline_token) |tok|
1421 return mod.failTok(scope, tok, "TODO inline for", .{});1473 return mod.failTok(scope, tok, "TODO inline for", .{});
14221474
...@@ -1458,6 +1510,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)...@@ -1458,6 +1510,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
1458 .decl = for_scope.decl,1510 .decl = for_scope.decl,
1459 .arena = for_scope.arena,1511 .arena = for_scope.arena,
1460 .instructions = .{},1512 .instructions = .{},
1513 .break_result_loc = rl,
1461 };1514 };
1462 defer loop_scope.instructions.deinit(mod.gpa);1515 defer loop_scope.instructions.deinit(mod.gpa);
14631516
...@@ -1499,6 +1552,14 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)...@@ -1499,6 +1552,14 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
1499 const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{1552 const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
1500 .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),1553 .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
1501 });1554 });
1555 loop_scope.break_block = for_block;
1556 loop_scope.continue_block = cond_block;
1557 if (for_node.label) |some| {
1558 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
1559 .token = some,
1560 .block_inst = for_block,
1561 });
1562 }
15021563
1503 // while body1564 // while body
1504 const then_src = tree.token_locs[for_node.body.lastToken()].start;1565 const then_src = tree.token_locs[for_node.body.lastToken()].start;
test/stage2/test.zig+92
...@@ -1204,4 +1204,96 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1204,4 +1204,96 @@ pub fn addCases(ctx: *TestContext) !void {
1204 \\extern var foo;1204 \\extern var foo;
1205 , &[_][]const u8{":4:1: error: unable to infer variable type"});1205 , &[_][]const u8{":4:1: error: unable to infer variable type"});
1206 }1206 }
1207
1208 {
1209 var case = ctx.exe("break/continue", linux_x64);
1210
1211 // Break out of loop
1212 case.addCompareOutput(
1213 \\export fn _start() noreturn {
1214 \\ while (true) {
1215 \\ break;
1216 \\ }
1217 \\
1218 \\ exit();
1219 \\}
1220 \\
1221 \\fn exit() noreturn {
1222 \\ asm volatile ("syscall"
1223 \\ :
1224 \\ : [number] "{rax}" (231),
1225 \\ [arg1] "{rdi}" (0)
1226 \\ : "rcx", "r11", "memory"
1227 \\ );
1228 \\ unreachable;
1229 \\}
1230 ,
1231 "",
1232 );
1233 case.addCompareOutput(
1234 \\export fn _start() noreturn {
1235 \\ foo: while (true) {
1236 \\ break :foo;
1237 \\ }
1238 \\
1239 \\ exit();
1240 \\}
1241 \\
1242 \\fn exit() noreturn {
1243 \\ asm volatile ("syscall"
1244 \\ :
1245 \\ : [number] "{rax}" (231),
1246 \\ [arg1] "{rdi}" (0)
1247 \\ : "rcx", "r11", "memory"
1248 \\ );
1249 \\ unreachable;
1250 \\}
1251 ,
1252 "",
1253 );
1254
1255 // Continue in loop
1256 case.addCompareOutput(
1257 \\export fn _start() noreturn {
1258 \\ var i: u64 = 0;
1259 \\ while (true) : (i+=1) {
1260 \\ if (i == 4) exit();
1261 \\ continue;
1262 \\ }
1263 \\}
1264 \\
1265 \\fn exit() noreturn {
1266 \\ asm volatile ("syscall"
1267 \\ :
1268 \\ : [number] "{rax}" (231),
1269 \\ [arg1] "{rdi}" (0)
1270 \\ : "rcx", "r11", "memory"
1271 \\ );
1272 \\ unreachable;
1273 \\}
1274 ,
1275 "",
1276 );
1277 case.addCompareOutput(
1278 \\export fn _start() noreturn {
1279 \\ var i: u64 = 0;
1280 \\ foo: while (true) : (i+=1) {
1281 \\ if (i == 4) exit();
1282 \\ continue :foo;
1283 \\ }
1284 \\}
1285 \\
1286 \\fn exit() noreturn {
1287 \\ asm volatile ("syscall"
1288 \\ :
1289 \\ : [number] "{rax}" (231),
1290 \\ [arg1] "{rdi}" (0)
1291 \\ : "rcx", "r11", "memory"
1292 \\ );
1293 \\ unreachable;
1294 \\}
1295 ,
1296 "",
1297 );
1298 }
1207}1299}