| ... | ... | @@ -261,6 +261,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 261 | 261 | .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), |
| 262 | 262 | .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block), |
| 263 | 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 | 265 | .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), |
| 265 | 266 | .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), |
| 266 | 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 | 292 | .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), |
| 292 | 293 | .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), |
| 293 | 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 | 295 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| 296 | 296 | .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), |
| 297 | 297 | .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 |
| 339 | 339 | const tree = parent_scope.tree(); |
| 340 | 340 | const src = tree.token_locs[node.ltoken].start; |
| 341 | 341 | |
| 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; |
| 369 | 355 | } |
| 370 | 356 | } |
| 357 | } else if (gen_zir.break_block) |inst| { |
| 358 | break :blk inst; |
| 371 | 359 | } |
| 372 | 360 | 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 | |
| 396 | fn 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 | }, |
| 381 | 434 | } |
| 382 | | } else { |
| 383 | | return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{}); |
| 384 | 435 | } |
| 385 | 436 | } |
| 386 | 437 | |
| ... | ... | @@ -426,16 +477,19 @@ fn labeledBlockExpr( |
| 426 | 477 | .decl = parent_scope.decl().?, |
| 427 | 478 | .arena = gen_zir.arena, |
| 428 | 479 | .instructions = .{}, |
| 480 | .break_result_loc = rl, |
| 429 | 481 | // TODO @as here is working around a stage1 miscompilation bug :( |
| 430 | 482 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 431 | 483 | .token = block_node.label, |
| 432 | 484 | .block_inst = block_inst, |
| 433 | | .result_loc = rl, |
| 434 | 485 | }), |
| 435 | 486 | }; |
| 436 | 487 | defer block_scope.instructions.deinit(mod.gpa); |
| 437 | 488 | |
| 438 | 489 | 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 | } |
| 439 | 493 | |
| 440 | 494 | block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); |
| 441 | 495 | 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 |
| 1289 | 1343 | } |
| 1290 | 1344 | } |
| 1291 | 1345 | |
| 1292 | | if (while_node.label) |tok| |
| 1293 | | return mod.failTok(scope, tok, "TODO labeled while", .{}); |
| 1294 | | |
| 1295 | 1346 | if (while_node.inline_token) |tok| |
| 1296 | 1347 | return mod.failTok(scope, tok, "TODO inline while", .{}); |
| 1297 | 1348 | |
| ... | ... | @@ -1308,6 +1359,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1308 | 1359 | .decl = expr_scope.decl, |
| 1309 | 1360 | .arena = expr_scope.arena, |
| 1310 | 1361 | .instructions = .{}, |
| 1362 | .break_result_loc = rl, |
| 1311 | 1363 | }; |
| 1312 | 1364 | defer loop_scope.instructions.deinit(mod.gpa); |
| 1313 | 1365 | |
| ... | ... | @@ -1348,6 +1400,14 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1348 | 1400 | const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{ |
| 1349 | 1401 | .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items), |
| 1350 | 1402 | }); |
| 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 | } |
| 1351 | 1411 | |
| 1352 | 1412 | const then_src = tree.token_locs[while_node.body.lastToken()].start; |
| 1353 | 1413 | var then_scope: Scope.GenZIR = .{ |
| ... | ... | @@ -1410,13 +1470,15 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1410 | 1470 | condbr.positionals.else_body = .{ |
| 1411 | 1471 | .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), |
| 1412 | 1472 | }; |
| 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 | } |
| 1413 | 1478 | return &while_block.base; |
| 1414 | 1479 | } |
| 1415 | 1480 | |
| 1416 | 1481 | fn 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 | 1482 | if (for_node.inline_token) |tok| |
| 1421 | 1483 | return mod.failTok(scope, tok, "TODO inline for", .{}); |
| 1422 | 1484 | |
| ... | ... | @@ -1458,6 +1520,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1458 | 1520 | .decl = for_scope.decl, |
| 1459 | 1521 | .arena = for_scope.arena, |
| 1460 | 1522 | .instructions = .{}, |
| 1523 | .break_result_loc = rl, |
| 1461 | 1524 | }; |
| 1462 | 1525 | defer loop_scope.instructions.deinit(mod.gpa); |
| 1463 | 1526 | |
| ... | ... | @@ -1499,6 +1562,14 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1499 | 1562 | const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{ |
| 1500 | 1563 | .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items), |
| 1501 | 1564 | }); |
| 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 | } |
| 1502 | 1573 | |
| 1503 | 1574 | // while body |
| 1504 | 1575 | 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) |
| 1585 | 1656 | condbr.positionals.else_body = .{ |
| 1586 | 1657 | .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), |
| 1587 | 1658 | }; |
| 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 | } |
| 1588 | 1664 | return &for_block.base; |
| 1589 | 1665 | } |
| 1590 | 1666 | |