authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-12-26 12:26:16+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-26 12:26:16+02:00
log641bf4c46eb2d5c1f3e95898ed74848a56e0d999
tree4604af2e79732db38955e44df5accc2c29c7e7de
parentd20174ad881ab98291c4d1733ca8ec92811abbdc
parenta50759325c95ad7dfd1fbf029759cf0a2608b3ab
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7552 from Vexu/stage2-continue

stage2 continue

3 files changed, 237 insertions(+), 46 deletions(-)

src/Module.zig+5-1
......@@ -796,11 +796,15 @@ pub const Scope = struct {
796796 /// The first N instructions in a function body ZIR are arg instructions.
797797 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
798798 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
800804 pub const Label = struct {
801805 token: ast.TokenIndex,
802806 block_inst: *zir.Inst.Block,
803 result_loc: astgen.ResultLoc,
807 used: bool = false,
804808 };
805809 };
806810
src/astgen.zig+121-45
......@@ -261,6 +261,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
261261 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
262262 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
263263 .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).?)),
264265 .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
265266 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
266267 .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
291292 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
292293 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
293294 .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", .{}),
295295 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
296296 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
297297 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
......@@ -339,48 +339,99 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
339339 const tree = parent_scope.tree();
340340 const src = tree.token_locs[node.ltoken].start;
341341
342 if (node.getLabel()) |break_label| {
343 // Look for the label in the scope.
344 var scope = parent_scope;
345 while (true) {
346 switch (scope.tag) {
347 .gen_zir => {
348 const gen_zir = scope.cast(Scope.GenZIR).?;
349 if (gen_zir.label) |label| {
350 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
351 if (node.getRHS()) |rhs| {
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 }, .{});
342 // Look for the label in the scope.
343 var scope = parent_scope;
344 while (true) {
345 switch (scope.tag) {
346 .gen_zir => {
347 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 label.used = true;
354 break :blk label.block_inst;
369355 }
370356 }
357 } else if (gen_zir.break_block) |inst| {
358 break :blk inst;
371359 }
372360 scope = gen_zir.parent;
373 },
374 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
375 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
376 else => {
377 const label_name = try identifierTokenString(mod, parent_scope, break_label);
378 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
379 },
380 }
361 continue;
362 };
363
364 if (node.getRHS()) |rhs| {
365 // Most result location types can be forwarded directly; however
366 // if we need to write to a pointer which has an inferred type,
367 // proper type inference requires peer type resolution on the block's
368 // break operand expressions.
369 const branch_rl: ResultLoc = switch (gen_zir.break_result_loc) {
370 .discard, .none, .ty, .ptr, .ref => gen_zir.break_result_loc,
371 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst },
372 };
373 const operand = try expr(mod, parent_scope, branch_rl, rhs);
374 return try addZIRInst(mod, parent_scope, src, zir.Inst.Break, .{
375 .block = block_inst,
376 .operand = operand,
377 }, .{});
378 } else {
379 return try addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{
380 .block = block_inst,
381 }, .{});
382 }
383 },
384 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
385 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
386 else => if (node.getLabel()) |break_label| {
387 const label_name = try identifierTokenString(mod, parent_scope, break_label);
388 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
389 } else {
390 return mod.failTok(parent_scope, src, "break expression outside loop", .{});
391 },
392 }
393 }
394}
395
396fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
397 const tree = parent_scope.tree();
398 const src = tree.token_locs[node.ltoken].start;
399
400 // Look for the label in the scope.
401 var scope = parent_scope;
402 while (true) {
403 switch (scope.tag) {
404 .gen_zir => {
405 const gen_zir = scope.cast(Scope.GenZIR).?;
406 const continue_block = gen_zir.continue_block orelse {
407 scope = gen_zir.parent;
408 continue;
409 };
410 if (node.getLabel()) |break_label| blk: {
411 if (gen_zir.label) |*label| {
412 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
413 label.used = true;
414 break :blk;
415 }
416 }
417 // found continue but either it has a different label, or no label
418 scope = gen_zir.parent;
419 continue;
420 }
421
422 return addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{
423 .block = continue_block,
424 }, .{});
425 },
426 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
427 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
428 else => if (node.getLabel()) |break_label| {
429 const label_name = try identifierTokenString(mod, parent_scope, break_label);
430 return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
431 } else {
432 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
433 },
381434 }
382 } else {
383 return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
384435 }
385436}
386437
......@@ -426,16 +477,19 @@ fn labeledBlockExpr(
426477 .decl = parent_scope.decl().?,
427478 .arena = gen_zir.arena,
428479 .instructions = .{},
480 .break_result_loc = rl,
429481 // TODO @as here is working around a stage1 miscompilation bug :(
430482 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
431483 .token = block_node.label,
432484 .block_inst = block_inst,
433 .result_loc = rl,
434485 }),
435486 };
436487 defer block_scope.instructions.deinit(mod.gpa);
437488
438489 try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
490 if (!block_scope.label.?.used) {
491 return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{});
492 }
439493
440494 block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
441495 try gen_zir.instructions.append(mod.gpa, &block_inst.base);
......@@ -1289,9 +1343,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
12891343 }
12901344 }
12911345
1292 if (while_node.label) |tok|
1293 return mod.failTok(scope, tok, "TODO labeled while", .{});
1294
12951346 if (while_node.inline_token) |tok|
12961347 return mod.failTok(scope, tok, "TODO inline while", .{});
12971348
......@@ -1308,6 +1359,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
13081359 .decl = expr_scope.decl,
13091360 .arena = expr_scope.arena,
13101361 .instructions = .{},
1362 .break_result_loc = rl,
13111363 };
13121364 defer loop_scope.instructions.deinit(mod.gpa);
13131365
......@@ -1348,6 +1400,14 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
13481400 const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
13491401 .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
13501402 });
1403 loop_scope.break_block = while_block;
1404 loop_scope.continue_block = cond_block;
1405 if (while_node.label) |some| {
1406 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
1407 .token = some,
1408 .block_inst = while_block,
1409 });
1410 }
13511411
13521412 const then_src = tree.token_locs[while_node.body.lastToken()].start;
13531413 var then_scope: Scope.GenZIR = .{
......@@ -1410,13 +1470,15 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
14101470 condbr.positionals.else_body = .{
14111471 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
14121472 };
1473 if (loop_scope.label) |some| {
1474 if (!some.used) {
1475 return mod.fail(scope, tree.token_locs[some.token].start, "unused while label", .{});
1476 }
1477 }
14131478 return &while_block.base;
14141479}
14151480
14161481fn 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
14201482 if (for_node.inline_token) |tok|
14211483 return mod.failTok(scope, tok, "TODO inline for", .{});
14221484
......@@ -1458,6 +1520,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
14581520 .decl = for_scope.decl,
14591521 .arena = for_scope.arena,
14601522 .instructions = .{},
1523 .break_result_loc = rl,
14611524 };
14621525 defer loop_scope.instructions.deinit(mod.gpa);
14631526
......@@ -1499,6 +1562,14 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
14991562 const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
15001563 .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
15011564 });
1565 loop_scope.break_block = for_block;
1566 loop_scope.continue_block = cond_block;
1567 if (for_node.label) |some| {
1568 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
1569 .token = some,
1570 .block_inst = for_block,
1571 });
1572 }
15021573
15031574 // while body
15041575 const then_src = tree.token_locs[for_node.body.lastToken()].start;
......@@ -1585,6 +1656,11 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
15851656 condbr.positionals.else_body = .{
15861657 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
15871658 };
1659 if (loop_scope.label) |some| {
1660 if (!some.used) {
1661 return mod.fail(scope, tree.token_locs[some.token].start, "unused for label", .{});
1662 }
1663 }
15881664 return &for_block.base;
15891665}
15901666
test/stage2/test.zig+111
......@@ -1219,4 +1219,115 @@ pub fn addCases(ctx: *TestContext) !void {
12191219 \\extern var foo;
12201220 , &[_][]const u8{":4:1: error: unable to infer variable type"});
12211221 }
1222
1223 {
1224 var case = ctx.exe("break/continue", linux_x64);
1225
1226 // Break out of loop
1227 case.addCompareOutput(
1228 \\export fn _start() noreturn {
1229 \\ while (true) {
1230 \\ break;
1231 \\ }
1232 \\
1233 \\ exit();
1234 \\}
1235 \\
1236 \\fn exit() noreturn {
1237 \\ asm volatile ("syscall"
1238 \\ :
1239 \\ : [number] "{rax}" (231),
1240 \\ [arg1] "{rdi}" (0)
1241 \\ : "rcx", "r11", "memory"
1242 \\ );
1243 \\ unreachable;
1244 \\}
1245 ,
1246 "",
1247 );
1248 case.addCompareOutput(
1249 \\export fn _start() noreturn {
1250 \\ foo: while (true) {
1251 \\ break :foo;
1252 \\ }
1253 \\
1254 \\ exit();
1255 \\}
1256 \\
1257 \\fn exit() noreturn {
1258 \\ asm volatile ("syscall"
1259 \\ :
1260 \\ : [number] "{rax}" (231),
1261 \\ [arg1] "{rdi}" (0)
1262 \\ : "rcx", "r11", "memory"
1263 \\ );
1264 \\ unreachable;
1265 \\}
1266 ,
1267 "",
1268 );
1269
1270 // Continue in loop
1271 case.addCompareOutput(
1272 \\export fn _start() noreturn {
1273 \\ var i: u64 = 0;
1274 \\ while (true) : (i+=1) {
1275 \\ if (i == 4) exit();
1276 \\ continue;
1277 \\ }
1278 \\}
1279 \\
1280 \\fn exit() noreturn {
1281 \\ asm volatile ("syscall"
1282 \\ :
1283 \\ : [number] "{rax}" (231),
1284 \\ [arg1] "{rdi}" (0)
1285 \\ : "rcx", "r11", "memory"
1286 \\ );
1287 \\ unreachable;
1288 \\}
1289 ,
1290 "",
1291 );
1292 case.addCompareOutput(
1293 \\export fn _start() noreturn {
1294 \\ var i: u64 = 0;
1295 \\ foo: while (true) : (i+=1) {
1296 \\ if (i == 4) exit();
1297 \\ continue :foo;
1298 \\ }
1299 \\}
1300 \\
1301 \\fn exit() noreturn {
1302 \\ asm volatile ("syscall"
1303 \\ :
1304 \\ : [number] "{rax}" (231),
1305 \\ [arg1] "{rdi}" (0)
1306 \\ : "rcx", "r11", "memory"
1307 \\ );
1308 \\ unreachable;
1309 \\}
1310 ,
1311 "",
1312 );
1313 }
1314
1315 {
1316 var case = ctx.exe("unused labels", linux_x64);
1317 case.addError(
1318 \\comptime {
1319 \\ foo: {}
1320 \\}
1321 , &[_][]const u8{":2:5: error: unused block label"});
1322 case.addError(
1323 \\comptime {
1324 \\ foo: while (true) {}
1325 \\}
1326 , &[_][]const u8{":2:5: error: unused while label"});
1327 case.addError(
1328 \\comptime {
1329 \\ foo: for ("foo") |_| {}
1330 \\}
1331 , &[_][]const u8{":2:5: error: unused for label"});
1332 }
12221333}