| ... | @@ -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,99 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr | ... | @@ -339,48 +339,99 @@ 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; |
| 341 | | 341 | |
| 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 | if (gen_zir.label) |label| { | 349 | const block_inst = blk: { |
| 350 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | 350 | if (node.getLabel()) |break_label| { |
| 351 | if (node.getRHS()) |rhs| { | 351 | if (gen_zir.label) |*label| { |
| 352 | // Most result location types can be forwarded directly; however | 352 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { |
| 353 | // if we need to write to a pointer which has an inferred type, | 353 | label.used = true; |
| 354 | // proper type inference requires peer type resolution on the block's | 354 | break :blk label.block_inst; |
| 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 | } | 355 | } |
| 370 | } | 356 | } |
| | 357 | } else if (gen_zir.break_block) |inst| { |
| | 358 | break :blk inst; |
| 371 | } | 359 | } |
| 372 | scope = gen_zir.parent; | 360 | scope = gen_zir.parent; |
| 373 | }, | 361 | continue; |
| 374 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | 362 | }; |
| 375 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | 363 | |
| 376 | else => { | 364 | if (node.getRHS()) |rhs| { |
| 377 | const label_name = try identifierTokenString(mod, parent_scope, break_label); | 365 | // Most result location types can be forwarded directly; however |
| 378 | return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); | 366 | // if we need to write to a pointer which has an inferred type, |
| 379 | }, | 367 | // proper type inference requires peer type resolution on the block's |
| 380 | } | 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,16 +477,19 @@ fn labeledBlockExpr( |
| 426 | .decl = parent_scope.decl().?, | 477 | .decl = parent_scope.decl().?, |
| 427 | .arena = gen_zir.arena, | 478 | .arena = gen_zir.arena, |
| 428 | .instructions = .{}, | 479 | .instructions = .{}, |
| | 480 | .break_result_loc = rl, |
| 429 | // TODO @as here is working around a stage1 miscompilation bug :( | 481 | // TODO @as here is working around a stage1 miscompilation bug :( |
| 430 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ | 482 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 431 | .token = block_node.label, | 483 | .token = block_node.label, |
| 432 | .block_inst = block_inst, | 484 | .block_inst = block_inst, |
| 433 | .result_loc = rl, | | |
| 434 | }), | 485 | }), |
| 435 | }; | 486 | }; |
| 436 | defer block_scope.instructions.deinit(mod.gpa); | 487 | defer block_scope.instructions.deinit(mod.gpa); |
| 437 | | 488 | |
| 438 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); | 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 | block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); | 494 | block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); |
| 441 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); | 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,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 | if (while_node.inline_token) |tok| | 1346 | if (while_node.inline_token) |tok| |
| 1296 | return mod.failTok(scope, tok, "TODO inline while", .{}); | 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,6 +1359,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1308 | .decl = expr_scope.decl, | 1359 | .decl = expr_scope.decl, |
| 1309 | .arena = expr_scope.arena, | 1360 | .arena = expr_scope.arena, |
| 1310 | .instructions = .{}, | 1361 | .instructions = .{}, |
| | 1362 | .break_result_loc = rl, |
| 1311 | }; | 1363 | }; |
| 1312 | defer loop_scope.instructions.deinit(mod.gpa); | 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,6 +1400,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, .{ | 1400 | const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{ |
| 1349 | .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items), | 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 | const then_src = tree.token_locs[while_node.body.lastToken()].start; | 1412 | const then_src = tree.token_locs[while_node.body.lastToken()].start; |
| 1353 | var then_scope: Scope.GenZIR = .{ | 1413 | var then_scope: Scope.GenZIR = .{ |
| ... | @@ -1410,13 +1470,15 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W | ... | @@ -1410,13 +1470,15 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1410 | condbr.positionals.else_body = .{ | 1470 | condbr.positionals.else_body = .{ |
| 1411 | .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), | 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 | return &while_block.base; | 1478 | return &while_block.base; |
| 1414 | } | 1479 | } |
| 1415 | | 1480 | |
| 1416 | fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst { | 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 | if (for_node.inline_token) |tok| | 1482 | if (for_node.inline_token) |tok| |
| 1421 | return mod.failTok(scope, tok, "TODO inline for", .{}); | 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,6 +1520,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1458 | .decl = for_scope.decl, | 1520 | .decl = for_scope.decl, |
| 1459 | .arena = for_scope.arena, | 1521 | .arena = for_scope.arena, |
| 1460 | .instructions = .{}, | 1522 | .instructions = .{}, |
| | 1523 | .break_result_loc = rl, |
| 1461 | }; | 1524 | }; |
| 1462 | defer loop_scope.instructions.deinit(mod.gpa); | 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,6 +1562,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, .{ | 1562 | const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{ |
| 1500 | .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items), | 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 | // while body | 1574 | // while body |
| 1504 | const then_src = tree.token_locs[for_node.body.lastToken()].start; | 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,6 +1656,11 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1585 | condbr.positionals.else_body = .{ | 1656 | condbr.positionals.else_body = .{ |
| 1586 | .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), | 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 | return &for_block.base; | 1664 | return &for_block.base; |
| 1589 | } | 1665 | } |
| 1590 | | 1666 | |