| ... | ... | @@ -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,97 @@ 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).?; |
| 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 | 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 | |
| 395 | fn 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 | 410 | if (gen_zir.label) |label| { |
| 350 | 411 | 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 | | }, .{}); |
| 369 | | } |
| 412 | break :blk; |
| 370 | 413 | } |
| 371 | 414 | } |
| 415 | // found continue but either it has a different label, or no label |
| 372 | 416 | 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 | | } |
| 417 | continue; |
| 418 | } |
| 419 | |
| 420 | return addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{ |
| 421 | .block = continue_block, |
| 422 | }, .{}); |
| 423 | }, |
| 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 | } |
| 386 | 435 | |
| ... | ... | @@ -426,11 +475,11 @@ fn labeledBlockExpr( |
| 426 | 475 | .decl = parent_scope.decl().?, |
| 427 | 476 | .arena = gen_zir.arena, |
| 428 | 477 | .instructions = .{}, |
| 478 | .break_result_loc = rl, |
| 429 | 479 | // TODO @as here is working around a stage1 miscompilation bug :( |
| 430 | 480 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 431 | 481 | .token = block_node.label, |
| 432 | 482 | .block_inst = block_inst, |
| 433 | | .result_loc = rl, |
| 434 | 483 | }), |
| 435 | 484 | }; |
| 436 | 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 | 1338 | } |
| 1290 | 1339 | } |
| 1291 | 1340 | |
| 1292 | | if (while_node.label) |tok| |
| 1293 | | return mod.failTok(scope, tok, "TODO labeled while", .{}); |
| 1294 | | |
| 1295 | 1341 | if (while_node.inline_token) |tok| |
| 1296 | 1342 | return mod.failTok(scope, tok, "TODO inline while", .{}); |
| 1297 | 1343 | |
| ... | ... | @@ -1308,6 +1354,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1308 | 1354 | .decl = expr_scope.decl, |
| 1309 | 1355 | .arena = expr_scope.arena, |
| 1310 | 1356 | .instructions = .{}, |
| 1357 | .break_result_loc = rl, |
| 1311 | 1358 | }; |
| 1312 | 1359 | defer loop_scope.instructions.deinit(mod.gpa); |
| 1313 | 1360 | |
| ... | ... | @@ -1348,6 +1395,14 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1348 | 1395 | const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{ |
| 1349 | 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 | } |
| 1351 | 1406 | |
| 1352 | 1407 | const then_src = tree.token_locs[while_node.body.lastToken()].start; |
| 1353 | 1408 | var then_scope: Scope.GenZIR = .{ |
| ... | ... | @@ -1414,9 +1469,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1414 | 1469 | } |
| 1415 | 1470 | |
| 1416 | 1471 | 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 | 1472 | if (for_node.inline_token) |tok| |
| 1421 | 1473 | return mod.failTok(scope, tok, "TODO inline for", .{}); |
| 1422 | 1474 | |
| ... | ... | @@ -1458,6 +1510,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1458 | 1510 | .decl = for_scope.decl, |
| 1459 | 1511 | .arena = for_scope.arena, |
| 1460 | 1512 | .instructions = .{}, |
| 1513 | .break_result_loc = rl, |
| 1461 | 1514 | }; |
| 1462 | 1515 | defer loop_scope.instructions.deinit(mod.gpa); |
| 1463 | 1516 | |
| ... | ... | @@ -1499,6 +1552,14 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1499 | 1552 | const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{ |
| 1500 | 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 | } |
| 1502 | 1563 | |
| 1503 | 1564 | // while body |
| 1504 | 1565 | const then_src = tree.token_locs[for_node.body.lastToken()].start; |