authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-31 20:58:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-31 21:09:22-07:00
log0f5eda973e0c17b3f792cdb06674bf8d2863c8fb
tree7a40c904b9246092009c02f5b09a2a8732b69175
parentde85c4ac429d5d7f937189cb444233952f7cbbed

stage2: delete astgen for switch expressions

The astgen for switch expressions did not respect the ZIR rules of only referencing instructions that are in scope: %14 = block_comptime_flat({ %15 = block_comptime_flat({ %16 = const(TypedValue{ .ty = comptime_int, .val = 1}) }) %17 = block_comptime_flat({ %18 = const(TypedValue{ .ty = comptime_int, .val = 2}) }) }) %19 = block({ %20 = ref(%5) %21 = deref(%20) %22 = switchbr(%20, [%15, %17], { %15 => { %23 = const(TypedValue{ .ty = comptime_int, .val = 1}) %24 = store(%10, %23) %25 = const(TypedValue{ .ty = void, .val = {}}) %26 = break("label_19", %25) }, %17 => { %27 = const(TypedValue{ .ty = comptime_int, .val = 2}) %28 = store(%10, %27) %29 = const(TypedValue{ .ty = void, .val = {}}) %30 = break("label_19", %29) } }, { %31 = unreachable_safe() }, special_prong=else) }) In this snippet you can see that the comptime expr referenced %15 and %17 which are not in scope. There also was no test coverage for runtime switch expressions. Switch expressions will have to be re-introduced to follow these rules and with some test coverage. There is some usable code being deleted in this commit; it will be useful to reference when re-implementing switch later. A few more improvements to do while we're at it: * only use .ref result loc on switch target if any prongs obtain the payload with |*syntax| - this improvement should be done to if, while, and for as well. - this will remove the needless ref/deref instructions above * remove switchbr and add switch_block, which is both a block and a switch branch. - similarly we should remove loop and add loop_block. This commit introduces a "force_comptime" flag into the GenZIR scope. The main purpose of this will be to choose the "comptime" variants of certain key zir instructions, such as function calls and branches. We will be moving away from using the block_comptime_flat ZIR instruction, and eventually deleting it. This commit also contains miscellaneous fixes to this branch that bring it to the state of passing all the tests.

5 files changed, 72 insertions(+), 612 deletions(-)

src/Module.zig+12-1
...@@ -375,6 +375,10 @@ pub const Scope = struct {...@@ -375,6 +375,10 @@ pub const Scope = struct {
375 }375 }
376 }376 }
377377
378 pub fn isComptime(self: *Scope) bool {
379 return self.getGenZIR().force_comptime;
380 }
381
378 pub fn ownerDecl(self: *Scope) ?*Decl {382 pub fn ownerDecl(self: *Scope) ?*Decl {
379 return switch (self.tag) {383 return switch (self.tag) {
380 .block => self.cast(Block).?.owner_decl,384 .block => self.cast(Block).?.owner_decl,
...@@ -712,6 +716,7 @@ pub const Scope = struct {...@@ -712,6 +716,7 @@ pub const Scope = struct {
712 parent: *Scope,716 parent: *Scope,
713 decl: *Decl,717 decl: *Decl,
714 arena: *Allocator,718 arena: *Allocator,
719 force_comptime: bool,
715 /// The first N instructions in a function body ZIR are arg instructions.720 /// The first N instructions in a function body ZIR are arg instructions.
716 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},721 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
717 label: ?Label = null,722 label: ?Label = null,
...@@ -1008,6 +1013,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1008,6 +1013,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1008 .decl = decl,1013 .decl = decl,
1009 .arena = &fn_type_scope_arena.allocator,1014 .arena = &fn_type_scope_arena.allocator,
1010 .parent = &decl.container.base,1015 .parent = &decl.container.base,
1016 .force_comptime = true,
1011 };1017 };
1012 defer fn_type_scope.instructions.deinit(self.gpa);1018 defer fn_type_scope.instructions.deinit(self.gpa);
10131019
...@@ -1171,6 +1177,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1171,6 +1177,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1171 .decl = decl,1177 .decl = decl,
1172 .arena = &decl_arena.allocator,1178 .arena = &decl_arena.allocator,
1173 .parent = &decl.container.base,1179 .parent = &decl.container.base,
1180 .force_comptime = false,
1174 };1181 };
1175 defer gen_scope.instructions.deinit(self.gpa);1182 defer gen_scope.instructions.deinit(self.gpa);
11761183
...@@ -1369,6 +1376,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1369,6 +1376,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1369 .decl = decl,1376 .decl = decl,
1370 .arena = &gen_scope_arena.allocator,1377 .arena = &gen_scope_arena.allocator,
1371 .parent = &decl.container.base,1378 .parent = &decl.container.base,
1379 .force_comptime = false,
1372 };1380 };
1373 defer gen_scope.instructions.deinit(self.gpa);1381 defer gen_scope.instructions.deinit(self.gpa);
13741382
...@@ -1428,6 +1436,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1428,6 +1436,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1428 .decl = decl,1436 .decl = decl,
1429 .arena = &type_scope_arena.allocator,1437 .arena = &type_scope_arena.allocator,
1430 .parent = &decl.container.base,1438 .parent = &decl.container.base,
1439 .force_comptime = true,
1431 };1440 };
1432 defer type_scope.instructions.deinit(self.gpa);1441 defer type_scope.instructions.deinit(self.gpa);
14331442
...@@ -1497,13 +1506,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1497,13 +1506,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
14971506
1498 decl.analysis = .in_progress;1507 decl.analysis = .in_progress;
14991508
1500 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.1509 // A comptime decl does not store any value so we can just deinit
1510 // this arena after analysis is done.
1501 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);1511 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
1502 defer analysis_arena.deinit();1512 defer analysis_arena.deinit();
1503 var gen_scope: Scope.GenZIR = .{1513 var gen_scope: Scope.GenZIR = .{
1504 .decl = decl,1514 .decl = decl,
1505 .arena = &analysis_arena.allocator,1515 .arena = &analysis_arena.allocator,
1506 .parent = &decl.container.base,1516 .parent = &decl.container.base,
1517 .force_comptime = true,
1507 };1518 };
1508 defer gen_scope.instructions.deinit(self.gpa);1519 defer gen_scope.instructions.deinit(self.gpa);
15091520
src/astgen.zig+54-288
...@@ -309,7 +309,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -309,7 +309,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
309 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),309 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
310 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),310 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
311 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),311 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
312 .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),312 .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
313 .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?),313 .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?),
314314
315 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),315 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
...@@ -334,11 +334,19 @@ fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.C...@@ -334,11 +334,19 @@ fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.C
334 return comptimeExpr(mod, scope, rl, node.expr);334 return comptimeExpr(mod, scope, rl, node.expr);
335}335}
336336
337pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {337pub fn comptimeExpr(
338 const tree = parent_scope.tree();338 mod: *Module,
339 const src = tree.token_locs[node.firstToken()].start;339 parent_scope: *Scope,
340 rl: ResultLoc,
341 node: *ast.Node,
342) InnerError!*zir.Inst {
343 // If we are already in a comptime scope, no need to make another one.
344 if (parent_scope.isComptime()) {
345 return expr(mod, parent_scope, rl, node);
346 }
340347
341 // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.348 // Optimization for labeled blocks: don't need to have 2 layers of blocks,
349 // we can reuse the existing one.
342 if (node.castTag(.LabeledBlock)) |block_node| {350 if (node.castTag(.LabeledBlock)) |block_node| {
343 return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);351 return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
344 }352 }
...@@ -348,6 +356,7 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as...@@ -348,6 +356,7 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as
348 .parent = parent_scope,356 .parent = parent_scope,
349 .decl = parent_scope.ownerDecl().?,357 .decl = parent_scope.ownerDecl().?,
350 .arena = parent_scope.arena(),358 .arena = parent_scope.arena(),
359 .force_comptime = true,
351 .instructions = .{},360 .instructions = .{},
352 };361 };
353 defer block_scope.instructions.deinit(mod.gpa);362 defer block_scope.instructions.deinit(mod.gpa);
...@@ -356,6 +365,9 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as...@@ -356,6 +365,9 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as
356 // instruction is the block's result value.365 // instruction is the block's result value.
357 _ = try expr(mod, &block_scope.base, rl, node);366 _ = try expr(mod, &block_scope.base, rl, node);
358367
368 const tree = parent_scope.tree();
369 const src = tree.token_locs[node.firstToken()].start;
370
359 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{371 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
360 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),372 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
361 });373 });
...@@ -410,7 +422,7 @@ fn breakExpr(...@@ -410,7 +422,7 @@ fn breakExpr(
410 try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?);422 try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?);
411423
412 if (have_store_to_block) {424 if (have_store_to_block) {
413 const inst_list = parent_scope.cast(Scope.GenZIR).?.instructions.items;425 const inst_list = parent_scope.getGenZIR().instructions.items;
414 const last_inst = inst_list[inst_list.len - 2];426 const last_inst = inst_list[inst_list.len - 2];
415 const store_inst = last_inst.castTag(.store_to_block_ptr).?;427 const store_inst = last_inst.castTag(.store_to_block_ptr).?;
416 assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?);428 assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?);
...@@ -559,6 +571,7 @@ fn labeledBlockExpr(...@@ -559,6 +571,7 @@ fn labeledBlockExpr(
559 .parent = parent_scope,571 .parent = parent_scope,
560 .decl = parent_scope.ownerDecl().?,572 .decl = parent_scope.ownerDecl().?,
561 .arena = gen_zir.arena,573 .arena = gen_zir.arena,
574 .force_comptime = parent_scope.isComptime(),
562 .instructions = .{},575 .instructions = .{},
563 // TODO @as here is working around a stage1 miscompilation bug :(576 // TODO @as here is working around a stage1 miscompilation bug :(
564 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{577 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
...@@ -746,6 +759,7 @@ fn varDecl(...@@ -746,6 +759,7 @@ fn varDecl(
746 .parent = scope,759 .parent = scope,
747 .decl = scope.ownerDecl().?,760 .decl = scope.ownerDecl().?,
748 .arena = scope.arena(),761 .arena = scope.arena(),
762 .force_comptime = scope.isComptime(),
749 .instructions = .{},763 .instructions = .{},
750 };764 };
751 defer init_scope.instructions.deinit(mod.gpa);765 defer init_scope.instructions.deinit(mod.gpa);
...@@ -1107,6 +1121,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con...@@ -1107,6 +1121,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1107 .parent = scope,1121 .parent = scope,
1108 .decl = scope.ownerDecl().?,1122 .decl = scope.ownerDecl().?,
1109 .arena = scope.arena(),1123 .arena = scope.arena(),
1124 .force_comptime = scope.isComptime(),
1110 .instructions = .{},1125 .instructions = .{},
1111 };1126 };
1112 defer gen_scope.instructions.deinit(mod.gpa);1127 defer gen_scope.instructions.deinit(mod.gpa);
...@@ -1343,6 +1358,7 @@ fn orelseCatchExpr(...@@ -1343,6 +1358,7 @@ fn orelseCatchExpr(
1343 .parent = scope,1358 .parent = scope,
1344 .decl = scope.ownerDecl().?,1359 .decl = scope.ownerDecl().?,
1345 .arena = scope.arena(),1360 .arena = scope.arena(),
1361 .force_comptime = scope.isComptime(),
1346 .instructions = .{},1362 .instructions = .{},
1347 };1363 };
1348 setBlockResultLoc(&block_scope, rl);1364 setBlockResultLoc(&block_scope, rl);
...@@ -1367,6 +1383,7 @@ fn orelseCatchExpr(...@@ -1367,6 +1383,7 @@ fn orelseCatchExpr(
1367 .parent = &block_scope.base,1383 .parent = &block_scope.base,
1368 .decl = block_scope.decl,1384 .decl = block_scope.decl,
1369 .arena = block_scope.arena,1385 .arena = block_scope.arena,
1386 .force_comptime = block_scope.force_comptime,
1370 .instructions = .{},1387 .instructions = .{},
1371 };1388 };
1372 defer then_scope.instructions.deinit(mod.gpa);1389 defer then_scope.instructions.deinit(mod.gpa);
...@@ -1395,6 +1412,7 @@ fn orelseCatchExpr(...@@ -1395,6 +1412,7 @@ fn orelseCatchExpr(
1395 .parent = &block_scope.base,1412 .parent = &block_scope.base,
1396 .decl = block_scope.decl,1413 .decl = block_scope.decl,
1397 .arena = block_scope.arena,1414 .arena = block_scope.arena,
1415 .force_comptime = block_scope.force_comptime,
1398 .instructions = .{},1416 .instructions = .{},
1399 };1417 };
1400 defer else_scope.instructions.deinit(mod.gpa);1418 defer else_scope.instructions.deinit(mod.gpa);
...@@ -1416,6 +1434,7 @@ fn orelseCatchExpr(...@@ -1416,6 +1434,7 @@ fn orelseCatchExpr(
1416 then_result,1434 then_result,
1417 unwrapped_payload,1435 unwrapped_payload,
1418 block,1436 block,
1437 block,
1419 );1438 );
1420}1439}
14211440
...@@ -1432,7 +1451,8 @@ fn finishThenElseBlock(...@@ -1432,7 +1451,8 @@ fn finishThenElseBlock(
1432 else_src: usize,1451 else_src: usize,
1433 then_result: *zir.Inst,1452 then_result: *zir.Inst,
1434 else_result: ?*zir.Inst,1453 else_result: ?*zir.Inst,
1435 block: *zir.Inst.Block,1454 main_block: *zir.Inst.Block,
1455 then_break_block: *zir.Inst.Block,
1436) InnerError!*zir.Inst {1456) InnerError!*zir.Inst {
1437 // We now have enough information to decide whether the result instruction should1457 // We now have enough information to decide whether the result instruction should
1438 // be communicated via result location pointer or break instructions.1458 // be communicated via result location pointer or break instructions.
...@@ -1441,42 +1461,42 @@ fn finishThenElseBlock(...@@ -1441,42 +1461,42 @@ fn finishThenElseBlock(
1441 .break_void => {1461 .break_void => {
1442 if (!then_result.tag.isNoReturn()) {1462 if (!then_result.tag.isNoReturn()) {
1443 _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{1463 _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{
1444 .block = block,1464 .block = then_break_block,
1445 });1465 });
1446 }1466 }
1447 if (else_result) |inst| {1467 if (else_result) |inst| {
1448 if (!inst.tag.isNoReturn()) {1468 if (!inst.tag.isNoReturn()) {
1449 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{1469 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1450 .block = block,1470 .block = main_block,
1451 });1471 });
1452 }1472 }
1453 } else {1473 } else {
1454 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{1474 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1455 .block = block,1475 .block = main_block,
1456 });1476 });
1457 }1477 }
1458 assert(!strat.elide_store_to_block_ptr_instructions);1478 assert(!strat.elide_store_to_block_ptr_instructions);
1459 try copyBodyNoEliding(then_body, then_scope.*);1479 try copyBodyNoEliding(then_body, then_scope.*);
1460 try copyBodyNoEliding(else_body, else_scope.*);1480 try copyBodyNoEliding(else_body, else_scope.*);
1461 return &block.base;1481 return &main_block.base;
1462 },1482 },
1463 .break_operand => {1483 .break_operand => {
1464 if (!then_result.tag.isNoReturn()) {1484 if (!then_result.tag.isNoReturn()) {
1465 _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{1485 _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{
1466 .block = block,1486 .block = then_break_block,
1467 .operand = then_result,1487 .operand = then_result,
1468 });1488 });
1469 }1489 }
1470 if (else_result) |inst| {1490 if (else_result) |inst| {
1471 if (!inst.tag.isNoReturn()) {1491 if (!inst.tag.isNoReturn()) {
1472 _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{1492 _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{
1473 .block = block,1493 .block = main_block,
1474 .operand = inst,1494 .operand = inst,
1475 });1495 });
1476 }1496 }
1477 } else {1497 } else {
1478 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{1498 _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
1479 .block = block,1499 .block = main_block,
1480 });1500 });
1481 }1501 }
1482 if (strat.elide_store_to_block_ptr_instructions) {1502 if (strat.elide_store_to_block_ptr_instructions) {
...@@ -1487,8 +1507,8 @@ fn finishThenElseBlock(...@@ -1487,8 +1507,8 @@ fn finishThenElseBlock(
1487 try copyBodyNoEliding(else_body, else_scope.*);1507 try copyBodyNoEliding(else_body, else_scope.*);
1488 }1508 }
1489 switch (rl) {1509 switch (rl) {
1490 .ref => return &block.base,1510 .ref => return &main_block.base,
1491 else => return rvalue(mod, parent_scope, rl, &block.base),1511 else => return rvalue(mod, parent_scope, rl, &main_block.base),
1492 }1512 }
1493 },1513 },
1494 }1514 }
...@@ -1643,6 +1663,7 @@ fn boolBinOp(...@@ -1643,6 +1663,7 @@ fn boolBinOp(
1643 .parent = scope,1663 .parent = scope,
1644 .decl = scope.ownerDecl().?,1664 .decl = scope.ownerDecl().?,
1645 .arena = scope.arena(),1665 .arena = scope.arena(),
1666 .force_comptime = scope.isComptime(),
1646 .instructions = .{},1667 .instructions = .{},
1647 };1668 };
1648 defer block_scope.instructions.deinit(mod.gpa);1669 defer block_scope.instructions.deinit(mod.gpa);
...@@ -1662,6 +1683,7 @@ fn boolBinOp(...@@ -1662,6 +1683,7 @@ fn boolBinOp(
1662 .parent = scope,1683 .parent = scope,
1663 .decl = block_scope.decl,1684 .decl = block_scope.decl,
1664 .arena = block_scope.arena,1685 .arena = block_scope.arena,
1686 .force_comptime = block_scope.force_comptime,
1665 .instructions = .{},1687 .instructions = .{},
1666 };1688 };
1667 defer rhs_scope.instructions.deinit(mod.gpa);1689 defer rhs_scope.instructions.deinit(mod.gpa);
...@@ -1676,6 +1698,7 @@ fn boolBinOp(...@@ -1676,6 +1698,7 @@ fn boolBinOp(
1676 .parent = scope,1698 .parent = scope,
1677 .decl = block_scope.decl,1699 .decl = block_scope.decl,
1678 .arena = block_scope.arena,1700 .arena = block_scope.arena,
1701 .force_comptime = block_scope.force_comptime,
1679 .instructions = .{},1702 .instructions = .{},
1680 };1703 };
1681 defer const_scope.instructions.deinit(mod.gpa);1704 defer const_scope.instructions.deinit(mod.gpa);
...@@ -1789,6 +1812,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1789,6 +1812,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1789 .parent = scope,1812 .parent = scope,
1790 .decl = scope.ownerDecl().?,1813 .decl = scope.ownerDecl().?,
1791 .arena = scope.arena(),1814 .arena = scope.arena(),
1815 .force_comptime = scope.isComptime(),
1792 .instructions = .{},1816 .instructions = .{},
1793 };1817 };
1794 setBlockResultLoc(&block_scope, rl);1818 setBlockResultLoc(&block_scope, rl);
...@@ -1813,6 +1837,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1813,6 +1837,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1813 .parent = scope,1837 .parent = scope,
1814 .decl = block_scope.decl,1838 .decl = block_scope.decl,
1815 .arena = block_scope.arena,1839 .arena = block_scope.arena,
1840 .force_comptime = block_scope.force_comptime,
1816 .instructions = .{},1841 .instructions = .{},
1817 };1842 };
1818 defer then_scope.instructions.deinit(mod.gpa);1843 defer then_scope.instructions.deinit(mod.gpa);
...@@ -1830,6 +1855,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1830,6 +1855,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1830 .parent = scope,1855 .parent = scope,
1831 .decl = block_scope.decl,1856 .decl = block_scope.decl,
1832 .arena = block_scope.arena,1857 .arena = block_scope.arena,
1858 .force_comptime = block_scope.force_comptime,
1833 .instructions = .{},1859 .instructions = .{},
1834 };1860 };
1835 defer else_scope.instructions.deinit(mod.gpa);1861 defer else_scope.instructions.deinit(mod.gpa);
...@@ -1863,6 +1889,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1863,6 +1889,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1863 then_result,1889 then_result,
1864 else_result,1890 else_result,
1865 block,1891 block,
1892 block,
1866 );1893 );
1867}1894}
18681895
...@@ -1912,6 +1939,7 @@ fn whileExpr(...@@ -1912,6 +1939,7 @@ fn whileExpr(
1912 .parent = scope,1939 .parent = scope,
1913 .decl = scope.ownerDecl().?,1940 .decl = scope.ownerDecl().?,
1914 .arena = scope.arena(),1941 .arena = scope.arena(),
1942 .force_comptime = scope.isComptime(),
1915 .instructions = .{},1943 .instructions = .{},
1916 };1944 };
1917 setBlockResultLoc(&loop_scope, rl);1945 setBlockResultLoc(&loop_scope, rl);
...@@ -1921,6 +1949,7 @@ fn whileExpr(...@@ -1921,6 +1949,7 @@ fn whileExpr(
1921 .parent = &loop_scope.base,1949 .parent = &loop_scope.base,
1922 .decl = loop_scope.decl,1950 .decl = loop_scope.decl,
1923 .arena = loop_scope.arena,1951 .arena = loop_scope.arena,
1952 .force_comptime = loop_scope.force_comptime,
1924 .instructions = .{},1953 .instructions = .{},
1925 };1954 };
1926 defer continue_scope.instructions.deinit(mod.gpa);1955 defer continue_scope.instructions.deinit(mod.gpa);
...@@ -1978,6 +2007,7 @@ fn whileExpr(...@@ -1978,6 +2007,7 @@ fn whileExpr(
1978 .parent = &continue_scope.base,2007 .parent = &continue_scope.base,
1979 .decl = continue_scope.decl,2008 .decl = continue_scope.decl,
1980 .arena = continue_scope.arena,2009 .arena = continue_scope.arena,
2010 .force_comptime = continue_scope.force_comptime,
1981 .instructions = .{},2011 .instructions = .{},
1982 };2012 };
1983 defer then_scope.instructions.deinit(mod.gpa);2013 defer then_scope.instructions.deinit(mod.gpa);
...@@ -1992,6 +2022,7 @@ fn whileExpr(...@@ -1992,6 +2022,7 @@ fn whileExpr(
1992 .parent = &continue_scope.base,2022 .parent = &continue_scope.base,
1993 .decl = continue_scope.decl,2023 .decl = continue_scope.decl,
1994 .arena = continue_scope.arena,2024 .arena = continue_scope.arena,
2025 .force_comptime = continue_scope.force_comptime,
1995 .instructions = .{},2026 .instructions = .{},
1996 };2027 };
1997 defer else_scope.instructions.deinit(mod.gpa);2028 defer else_scope.instructions.deinit(mod.gpa);
...@@ -2027,6 +2058,7 @@ fn whileExpr(...@@ -2027,6 +2058,7 @@ fn whileExpr(
2027 then_result,2058 then_result,
2028 else_result,2059 else_result,
2029 while_block,2060 while_block,
2061 cond_block,
2030 );2062 );
2031}2063}
20322064
...@@ -2068,6 +2100,7 @@ fn forExpr(...@@ -2068,6 +2100,7 @@ fn forExpr(
2068 .parent = scope,2100 .parent = scope,
2069 .decl = scope.ownerDecl().?,2101 .decl = scope.ownerDecl().?,
2070 .arena = scope.arena(),2102 .arena = scope.arena(),
2103 .force_comptime = scope.isComptime(),
2071 .instructions = .{},2104 .instructions = .{},
2072 };2105 };
2073 setBlockResultLoc(&loop_scope, rl);2106 setBlockResultLoc(&loop_scope, rl);
...@@ -2077,6 +2110,7 @@ fn forExpr(...@@ -2077,6 +2110,7 @@ fn forExpr(
2077 .parent = &loop_scope.base,2110 .parent = &loop_scope.base,
2078 .decl = loop_scope.decl,2111 .decl = loop_scope.decl,
2079 .arena = loop_scope.arena,2112 .arena = loop_scope.arena,
2113 .force_comptime = loop_scope.force_comptime,
2080 .instructions = .{},2114 .instructions = .{},
2081 };2115 };
2082 defer cond_scope.instructions.deinit(mod.gpa);2116 defer cond_scope.instructions.deinit(mod.gpa);
...@@ -2134,6 +2168,7 @@ fn forExpr(...@@ -2134,6 +2168,7 @@ fn forExpr(
2134 .parent = &cond_scope.base,2168 .parent = &cond_scope.base,
2135 .decl = cond_scope.decl,2169 .decl = cond_scope.decl,
2136 .arena = cond_scope.arena,2170 .arena = cond_scope.arena,
2171 .force_comptime = cond_scope.force_comptime,
2137 .instructions = .{},2172 .instructions = .{},
2138 };2173 };
2139 defer then_scope.instructions.deinit(mod.gpa);2174 defer then_scope.instructions.deinit(mod.gpa);
...@@ -2174,6 +2209,7 @@ fn forExpr(...@@ -2174,6 +2209,7 @@ fn forExpr(
2174 .parent = &cond_scope.base,2209 .parent = &cond_scope.base,
2175 .decl = cond_scope.decl,2210 .decl = cond_scope.decl,
2176 .arena = cond_scope.arena,2211 .arena = cond_scope.arena,
2212 .force_comptime = cond_scope.force_comptime,
2177 .instructions = .{},2213 .instructions = .{},
2178 };2214 };
2179 defer else_scope.instructions.deinit(mod.gpa);2215 defer else_scope.instructions.deinit(mod.gpa);
...@@ -2206,281 +2242,10 @@ fn forExpr(...@@ -2206,281 +2242,10 @@ fn forExpr(
2206 then_result,2242 then_result,
2207 else_result,2243 else_result,
2208 for_block,2244 for_block,
2245 cond_block,
2209 );2246 );
2210}2247}
22112248
2212fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp {
2213 var cur = node;
2214 while (true) {
2215 switch (cur.tag) {
2216 .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur),
2217 .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr,
2218 else => return null,
2219 }
2220 }
2221}
2222
2223fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst {
2224 if (true) {
2225 @panic("TODO reimplement this");
2226 }
2227 var block_scope: Scope.GenZIR = .{
2228 .parent = scope,
2229 .decl = scope.ownerDecl().?,
2230 .arena = scope.arena(),
2231 .instructions = .{},
2232 };
2233 defer block_scope.instructions.deinit(mod.gpa);
2234
2235 const tree = scope.tree();
2236 const switch_src = tree.token_locs[switch_node.switch_token].start;
2237 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
2238 const target = try addZIRUnOp(mod, &block_scope.base, target_ptr.src, .deref, target_ptr);
2239 // Add the switch instruction here so that it comes before any range checks.
2240 const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
2241 .target_ptr = target_ptr,
2242 .cases = undefined, // populated below
2243 .items = &[_]*zir.Inst{}, // populated below
2244 .else_body = undefined, // populated below
2245 }, .{})).castTag(.switchbr).?;
2246
2247 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
2248 defer items.deinit();
2249 var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa);
2250 defer cases.deinit();
2251
2252 // Add comptime block containing all prong items first,
2253 const item_block = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{
2254 .instructions = undefined, // populated below
2255 });
2256 // then add block containing the switch.
2257 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
2258 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
2259 });
2260
2261 // Most result location types can be forwarded directly; however
2262 // if we need to write to a pointer which has an inferred type,
2263 // proper type inference requires peer type resolution on the switch case.
2264 const case_rl: ResultLoc = switch (rl) {
2265 .discard, .none, .ty, .ptr, .ref => rl,
2266 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
2267 };
2268
2269 var item_scope: Scope.GenZIR = .{
2270 .parent = scope,
2271 .decl = scope.ownerDecl().?,
2272 .arena = scope.arena(),
2273 .instructions = .{},
2274 };
2275 defer item_scope.instructions.deinit(mod.gpa);
2276
2277 var case_scope: Scope.GenZIR = .{
2278 .parent = scope,
2279 .decl = block_scope.decl,
2280 .arena = block_scope.arena,
2281 .instructions = .{},
2282 };
2283 defer case_scope.instructions.deinit(mod.gpa);
2284
2285 var else_scope: Scope.GenZIR = .{
2286 .parent = scope,
2287 .decl = block_scope.decl,
2288 .arena = block_scope.arena,
2289 .instructions = .{},
2290 };
2291 defer else_scope.instructions.deinit(mod.gpa);
2292
2293 // first we gather all the switch items and check else/'_' prongs
2294 var else_src: ?usize = null;
2295 var underscore_src: ?usize = null;
2296 var first_range: ?*zir.Inst = null;
2297 var special_case: ?*ast.Node.SwitchCase = null;
2298 for (switch_node.cases()) |uncasted_case| {
2299 const case = uncasted_case.castTag(.SwitchCase).?;
2300 const case_src = tree.token_locs[case.firstToken()].start;
2301 // reset without freeing to reduce allocations.
2302 case_scope.instructions.items.len = 0;
2303 assert(case.items_len != 0);
2304
2305 // Check for else/_ prong, those are handled last.
2306 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
2307 if (else_src) |src| {
2308 const msg = msg: {
2309 const msg = try mod.errMsg(
2310 scope,
2311 case_src,
2312 "multiple else prongs in switch expression",
2313 .{},
2314 );
2315 errdefer msg.destroy(mod.gpa);
2316 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2317 break :msg msg;
2318 };
2319 return mod.failWithOwnedErrorMsg(scope, msg);
2320 }
2321 else_src = case_src;
2322 special_case = case;
2323 continue;
2324 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
2325 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
2326 {
2327 if (underscore_src) |src| {
2328 const msg = msg: {
2329 const msg = try mod.errMsg(
2330 scope,
2331 case_src,
2332 "multiple '_' prongs in switch expression",
2333 .{},
2334 );
2335 errdefer msg.destroy(mod.gpa);
2336 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2337 break :msg msg;
2338 };
2339 return mod.failWithOwnedErrorMsg(scope, msg);
2340 }
2341 underscore_src = case_src;
2342 special_case = case;
2343 continue;
2344 }
2345
2346 if (else_src) |some_else| {
2347 if (underscore_src) |some_underscore| {
2348 const msg = msg: {
2349 const msg = try mod.errMsg(
2350 scope,
2351 switch_src,
2352 "else and '_' prong in switch expression",
2353 .{},
2354 );
2355 errdefer msg.destroy(mod.gpa);
2356 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2357 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2358 break :msg msg;
2359 };
2360 return mod.failWithOwnedErrorMsg(scope, msg);
2361 }
2362 }
2363
2364 // If this is a simple one item prong then it is handled by the switchbr.
2365 if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) {
2366 const item = try expr(mod, &item_scope.base, .none, case.items()[0]);
2367 try items.append(item);
2368 try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
2369
2370 try cases.append(.{
2371 .item = item,
2372 .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) },
2373 });
2374 continue;
2375 }
2376
2377 // TODO if the case has few items and no ranges it might be better
2378 // to just handle them as switch prongs.
2379
2380 // Check if the target matches any of the items.
2381 // 1, 2, 3..6 will result in
2382 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2383 var any_ok: ?*zir.Inst = null;
2384 for (case.items()) |item| {
2385 if (getRangeNode(item)) |range| {
2386 const start = try expr(mod, &item_scope.base, .none, range.lhs);
2387 const end = try expr(mod, &item_scope.base, .none, range.rhs);
2388 const range_src = tree.token_locs[range.op_token].start;
2389 const range_inst = try addZIRBinOp(mod, &item_scope.base, range_src, .switch_range, start, end);
2390 try items.append(range_inst);
2391 if (first_range == null) first_range = range_inst;
2392
2393 // target >= start and target <= end
2394 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start);
2395 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end);
2396 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok);
2397
2398 if (any_ok) |some| {
2399 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok);
2400 } else {
2401 any_ok = range_ok;
2402 }
2403 continue;
2404 }
2405
2406 const item_inst = try expr(mod, &item_scope.base, .none, item);
2407 try items.append(item_inst);
2408 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
2409
2410 if (any_ok) |some| {
2411 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok);
2412 } else {
2413 any_ok = cpm_ok;
2414 }
2415 }
2416
2417 const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
2418 .condition = any_ok.?,
2419 .then_body = undefined, // populated below
2420 .else_body = undefined, // populated below
2421 }, .{});
2422 const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
2423 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2424 });
2425
2426 // reset cond_scope for then_body
2427 case_scope.instructions.items.len = 0;
2428 try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
2429 condbr.positionals.then_body = .{
2430 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2431 };
2432
2433 // reset cond_scope for else_body
2434 case_scope.instructions.items.len = 0;
2435 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
2436 .block = cond_block,
2437 }, .{});
2438 condbr.positionals.else_body = .{
2439 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2440 };
2441 }
2442
2443 // Generate else block or a break last to finish the block.
2444 if (special_case) |case| {
2445 try switchCaseExpr(mod, &else_scope.base, case_rl, block, case);
2446 } else {
2447 // Not handling all possible cases is a compile error.
2448 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
2449 }
2450
2451 // All items have been generated, add the instructions to the comptime block.
2452 item_block.positionals.body = .{
2453 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
2454 };
2455
2456 // Actually populate switch instruction values.
2457 if (else_src != null) switch_inst.kw_args.special_prong = .@"else";
2458 if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore;
2459 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
2460 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
2461 switch_inst.kw_args.range = first_range;
2462 switch_inst.positionals.else_body = .{
2463 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
2464 };
2465 return &block.base;
2466}
2467
2468fn switchCaseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, case: *ast.Node.SwitchCase) !void {
2469 const tree = scope.tree();
2470 const case_src = tree.token_locs[case.firstToken()].start;
2471 if (case.payload != null) {
2472 return mod.fail(scope, case_src, "TODO switch case payload capture", .{});
2473 }
2474
2475 const case_body = try expr(mod, scope, rl, case.expr);
2476 if (!case_body.tag.isNoReturn()) {
2477 _ = try addZIRInst(mod, scope, case_src, zir.Inst.Break, .{
2478 .block = block,
2479 .operand = case_body,
2480 }, .{});
2481 }
2482}
2483
2484fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {2249fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
2485 const tree = scope.tree();2250 const tree = scope.tree();
2486 const src = tree.token_locs[cfe.ltoken].start;2251 const src = tree.token_locs[cfe.ltoken].start;
...@@ -2859,6 +2624,7 @@ fn asRlPtr(...@@ -2859,6 +2624,7 @@ fn asRlPtr(
2859 .parent = scope,2624 .parent = scope,
2860 .decl = scope.ownerDecl().?,2625 .decl = scope.ownerDecl().?,
2861 .arena = scope.arena(),2626 .arena = scope.arena(),
2627 .force_comptime = scope.isComptime(),
2862 .instructions = .{},2628 .instructions = .{},
2863 };2629 };
2864 defer as_scope.instructions.deinit(mod.gpa);2630 defer as_scope.instructions.deinit(mod.gpa);
src/zir.zig-56
...@@ -336,12 +336,6 @@ pub const Inst = struct {...@@ -336,12 +336,6 @@ pub const Inst = struct {
336 enum_literal,336 enum_literal,
337 /// Create an enum type.337 /// Create an enum type.
338 enum_type,338 enum_type,
339 /// A switch expression.
340 switchbr,
341 /// A range in a switch case, `lhs...rhs`.
342 /// Only checks that `lhs >= rhs` if they are ints, everything else is
343 /// validated by the .switch instruction.
344 switch_range,
345 /// Does nothing; returns a void value.339 /// Does nothing; returns a void value.
346 void_value,340 void_value,
347341
...@@ -441,7 +435,6 @@ pub const Inst = struct {...@@ -441,7 +435,6 @@ pub const Inst = struct {
441 .error_union_type,435 .error_union_type,
442 .merge_error_sets,436 .merge_error_sets,
443 .slice_start,437 .slice_start,
444 .switch_range,
445 => BinOp,438 => BinOp,
446439
447 .block,440 .block,
...@@ -478,7 +471,6 @@ pub const Inst = struct {...@@ -478,7 +471,6 @@ pub const Inst = struct {
478 .enum_literal => EnumLiteral,471 .enum_literal => EnumLiteral,
479 .error_set => ErrorSet,472 .error_set => ErrorSet,
480 .slice => Slice,473 .slice => Slice,
481 .switchbr => SwitchBr,
482 .typeof_peer => TypeOfPeer,474 .typeof_peer => TypeOfPeer,
483 .container_field_named => ContainerFieldNamed,475 .container_field_named => ContainerFieldNamed,
484 .container_field_typed => ContainerFieldTyped,476 .container_field_typed => ContainerFieldTyped,
...@@ -605,7 +597,6 @@ pub const Inst = struct {...@@ -605,7 +597,6 @@ pub const Inst = struct {
605 .slice,597 .slice,
606 .slice_start,598 .slice_start,
607 .import,599 .import,
608 .switch_range,
609 .typeof_peer,600 .typeof_peer,
610 .resolve_inferred_alloc,601 .resolve_inferred_alloc,
611 .set_eval_branch_quota,602 .set_eval_branch_quota,
...@@ -625,7 +616,6 @@ pub const Inst = struct {...@@ -625,7 +616,6 @@ pub const Inst = struct {
625 .unreachable_unsafe,616 .unreachable_unsafe,
626 .unreachable_safe,617 .unreachable_safe,
627 .loop,618 .loop,
628 .switchbr,
629 .container_field_named,619 .container_field_named,
630 .container_field_typed,620 .container_field_typed,
631 .container_field,621 .container_field,
...@@ -1091,32 +1081,6 @@ pub const Inst = struct {...@@ -1091,32 +1081,6 @@ pub const Inst = struct {
1091 },1081 },
1092 };1082 };
10931083
1094 pub const SwitchBr = struct {
1095 pub const base_tag = Tag.switchbr;
1096 base: Inst,
1097
1098 positionals: struct {
1099 target_ptr: *Inst,
1100 /// List of all individual items and ranges
1101 items: []*Inst,
1102 cases: []Case,
1103 else_body: Body,
1104 },
1105 kw_args: struct {
1106 /// Pointer to first range if such exists.
1107 range: ?*Inst = null,
1108 special_prong: enum {
1109 none,
1110 @"else",
1111 underscore,
1112 } = .none,
1113 },
1114
1115 pub const Case = struct {
1116 item: *Inst,
1117 body: Body,
1118 };
1119 };
1120 pub const TypeOfPeer = struct {1084 pub const TypeOfPeer = struct {
1121 pub const base_tag = .typeof_peer;1085 pub const base_tag = .typeof_peer;
1122 base: Inst,1086 base: Inst,
...@@ -1467,26 +1431,6 @@ const Writer = struct {...@@ -1467,26 +1431,6 @@ const Writer = struct {
1467 }1431 }
1468 try stream.writeByte(']');1432 try stream.writeByte(']');
1469 },1433 },
1470 []Inst.SwitchBr.Case => {
1471 if (param.len == 0) {
1472 return stream.writeAll("{}");
1473 }
1474 try stream.writeAll("{\n");
1475 for (param) |*case, i| {
1476 if (i != 0) {
1477 try stream.writeAll(",\n");
1478 }
1479 try stream.writeByteNTimes(' ', self.indent);
1480 self.indent += 2;
1481 try self.writeParamToStream(stream, &case.item);
1482 try stream.writeAll(" => ");
1483 try self.writeParamToStream(stream, &case.body);
1484 self.indent -= 2;
1485 }
1486 try stream.writeByte('\n');
1487 try stream.writeByteNTimes(' ', self.indent - 2);
1488 try stream.writeByte('}');
1489 },
1490 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),1434 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
1491 }1435 }
1492 }1436 }
src/zir_sema.zig+6-230
...@@ -151,8 +151,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -151,8 +151,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
151 .slice => return zirSlice(mod, scope, old_inst.castTag(.slice).?),151 .slice => return zirSlice(mod, scope, old_inst.castTag(.slice).?),
152 .slice_start => return zirSliceStart(mod, scope, old_inst.castTag(.slice_start).?),152 .slice_start => return zirSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
153 .import => return zirImport(mod, scope, old_inst.castTag(.import).?),153 .import => return zirImport(mod, scope, old_inst.castTag(.import).?),
154 .switchbr => return zirSwitchbr(mod, scope, old_inst.castTag(.switchbr).?),
155 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
156 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),154 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),
157 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),155 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),
158 .void_value => return mod.constVoid(scope, old_inst.src),156 .void_value => return mod.constVoid(scope, old_inst.src),
...@@ -795,6 +793,12 @@ fn analyzeBlockBody(...@@ -795,6 +793,12 @@ fn analyzeBlockBody(
795 var coerce_block = parent_block.makeSubBlock();793 var coerce_block = parent_block.makeSubBlock();
796 defer coerce_block.instructions.deinit(mod.gpa);794 defer coerce_block.instructions.deinit(mod.gpa);
797 const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand);795 const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand);
796 // If no instructions were produced, such as in the case of a coercion of a
797 // constant value to a new type, we can simply point the br operand to it.
798 if (coerce_block.instructions.items.len == 0) {
799 br.operand = coerced_operand;
800 continue;
801 }
798 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);802 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
799 // Here we depend on the br instruction having been over-allocated (if necessary)803 // Here we depend on the br instruction having been over-allocated (if necessary)
800 // inide analyzeBreak so that it can be converted into a br_block_flat instruction.804 // inide analyzeBreak so that it can be converted into a br_block_flat instruction.
...@@ -1531,234 +1535,6 @@ fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!...@@ -1531,234 +1535,6 @@ fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!
1531 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);1535 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
1532}1536}
15331537
1534fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1535 const tracy = trace(@src());
1536 defer tracy.end();
1537 const start = try resolveInst(mod, scope, inst.positionals.lhs);
1538 const end = try resolveInst(mod, scope, inst.positionals.rhs);
1539
1540 switch (start.ty.zigTypeTag()) {
1541 .Int, .ComptimeInt => {},
1542 else => return mod.constVoid(scope, inst.base.src),
1543 }
1544 switch (end.ty.zigTypeTag()) {
1545 .Int, .ComptimeInt => {},
1546 else => return mod.constVoid(scope, inst.base.src),
1547 }
1548 if (start.value()) |start_val| {
1549 if (end.value()) |end_val| {
1550 if (start_val.compare(.gte, end_val)) {
1551 return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{});
1552 }
1553 }
1554 }
1555 return mod.constVoid(scope, inst.base.src);
1556}
1557
1558fn zirSwitchbr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst {
1559 const tracy = trace(@src());
1560 defer tracy.end();
1561 const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr);
1562 const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
1563 try validateSwitch(mod, scope, target, inst);
1564
1565 if (try mod.resolveDefinedValue(scope, target)) |target_val| {
1566 for (inst.positionals.cases) |case| {
1567 const resolved = try resolveInst(mod, scope, case.item);
1568 const casted = try mod.coerce(scope, target.ty, resolved);
1569 const item = try mod.resolveConstValue(scope, casted);
1570
1571 if (target_val.eql(item)) {
1572 try analyzeBody(mod, scope.cast(Scope.Block).?, case.body);
1573 return mod.constNoReturn(scope, inst.base.src);
1574 }
1575 }
1576 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1577 return mod.constNoReturn(scope, inst.base.src);
1578 }
1579
1580 if (inst.positionals.cases.len == 0) {
1581 // no cases just analyze else_branch
1582 try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body);
1583 return mod.constNoReturn(scope, inst.base.src);
1584 }
1585
1586 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1587 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
1588
1589 var case_block: Scope.Block = .{
1590 .parent = parent_block,
1591 .inst_table = parent_block.inst_table,
1592 .func = parent_block.func,
1593 .owner_decl = parent_block.owner_decl,
1594 .src_decl = parent_block.src_decl,
1595 .instructions = .{},
1596 .arena = parent_block.arena,
1597 .inlining = parent_block.inlining,
1598 .is_comptime = parent_block.is_comptime,
1599 .branch_quota = parent_block.branch_quota,
1600 };
1601 defer case_block.instructions.deinit(mod.gpa);
1602
1603 for (inst.positionals.cases) |case, i| {
1604 // Reset without freeing.
1605 case_block.instructions.items.len = 0;
1606
1607 const resolved = try resolveInst(mod, scope, case.item);
1608 const casted = try mod.coerce(scope, target.ty, resolved);
1609 const item = try mod.resolveConstValue(scope, casted);
1610
1611 try analyzeBody(mod, &case_block, case.body);
1612
1613 cases[i] = .{
1614 .item = item,
1615 .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },
1616 };
1617 }
1618
1619 case_block.instructions.items.len = 0;
1620 try analyzeBody(mod, &case_block, inst.positionals.else_body);
1621
1622 const else_body: ir.Body = .{
1623 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
1624 };
1625
1626 return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);
1627}
1628
1629fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
1630 // validate usage of '_' prongs
1631 if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
1632 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
1633 // TODO notes "'_' prong here" inst.positionals.cases[last].src
1634 }
1635
1636 // check that target type supports ranges
1637 if (inst.kw_args.range) |range_inst| {
1638 switch (target.ty.zigTypeTag()) {
1639 .Int, .ComptimeInt => {},
1640 else => {
1641 return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty});
1642 // TODO notes "range used here" range_inst.src
1643 },
1644 }
1645 }
1646
1647 // validate for duplicate items/missing else prong
1648 switch (target.ty.zigTypeTag()) {
1649 .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}),
1650 .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),
1651 .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}),
1652 .Int, .ComptimeInt => {
1653 var range_set = @import("RangeSet.zig").init(mod.gpa);
1654 defer range_set.deinit();
1655
1656 for (inst.positionals.items) |item| {
1657 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {
1658 const start_resolved = try resolveInst(mod, scope, range.positionals.lhs);
1659 const start_casted = try mod.coerce(scope, target.ty, start_resolved);
1660 const end_resolved = try resolveInst(mod, scope, range.positionals.rhs);
1661 const end_casted = try mod.coerce(scope, target.ty, end_resolved);
1662
1663 break :blk try range_set.add(
1664 try mod.resolveConstValue(scope, start_casted),
1665 try mod.resolveConstValue(scope, end_casted),
1666 item.src,
1667 );
1668 } else blk: {
1669 const resolved = try resolveInst(mod, scope, item);
1670 const casted = try mod.coerce(scope, target.ty, resolved);
1671 const value = try mod.resolveConstValue(scope, casted);
1672 break :blk try range_set.add(value, value, item.src);
1673 };
1674
1675 if (maybe_src) |previous_src| {
1676 return mod.fail(scope, item.src, "duplicate switch value", .{});
1677 // TODO notes "previous value is here" previous_src
1678 }
1679 }
1680
1681 if (target.ty.zigTypeTag() == .Int) {
1682 var arena = std.heap.ArenaAllocator.init(mod.gpa);
1683 defer arena.deinit();
1684
1685 const start = try target.ty.minInt(&arena, mod.getTarget());
1686 const end = try target.ty.maxInt(&arena, mod.getTarget());
1687 if (try range_set.spans(start, end)) {
1688 if (inst.kw_args.special_prong == .@"else") {
1689 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1690 }
1691 return;
1692 }
1693 }
1694
1695 if (inst.kw_args.special_prong != .@"else") {
1696 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1697 }
1698 },
1699 .Bool => {
1700 var true_count: u8 = 0;
1701 var false_count: u8 = 0;
1702 for (inst.positionals.items) |item| {
1703 const resolved = try resolveInst(mod, scope, item);
1704 const casted = try mod.coerce(scope, Type.initTag(.bool), resolved);
1705 if ((try mod.resolveConstValue(scope, casted)).toBool()) {
1706 true_count += 1;
1707 } else {
1708 false_count += 1;
1709 }
1710
1711 if (true_count + false_count > 2) {
1712 return mod.fail(scope, item.src, "duplicate switch value", .{});
1713 }
1714 }
1715 if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") {
1716 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1717 }
1718 if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") {
1719 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1720 }
1721 },
1722 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
1723 if (inst.kw_args.special_prong != .@"else") {
1724 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
1725 }
1726
1727 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa);
1728 defer seen_values.deinit();
1729
1730 for (inst.positionals.items) |item| {
1731 const resolved = try resolveInst(mod, scope, item);
1732 const casted = try mod.coerce(scope, target.ty, resolved);
1733 const val = try mod.resolveConstValue(scope, casted);
1734
1735 if (try seen_values.fetchPut(val, item.src)) |prev| {
1736 return mod.fail(scope, item.src, "duplicate switch value", .{});
1737 // TODO notes "previous value here" prev.value
1738 }
1739 }
1740 },
1741
1742 .ErrorUnion,
1743 .NoReturn,
1744 .Array,
1745 .Struct,
1746 .Undefined,
1747 .Null,
1748 .Optional,
1749 .BoundFn,
1750 .Opaque,
1751 .Vector,
1752 .Frame,
1753 .AnyFrame,
1754 .ComptimeFloat,
1755 .Float,
1756 => {
1757 return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty});
1758 },
1759 }
1760}
1761
1762fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1538fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1763 const tracy = trace(@src());1539 const tracy = trace(@src());
1764 defer tracy.end();1540 defer tracy.end();
test/stage2/test.zig-37
...@@ -962,43 +962,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -962,43 +962,6 @@ pub fn addCases(ctx: *TestContext) !void {
962 ,962 ,
963 "hello\nhello\nhello\nhello\nhello\n",963 "hello\nhello\nhello\nhello\nhello\n",
964 );964 );
965
966 // comptime switch
967
968 // Basic for loop
969 case.addCompareOutput(
970 \\pub export fn _start() noreturn {
971 \\ assert(foo() == 1);
972 \\ exit();
973 \\}
974 \\
975 \\fn foo() u32 {
976 \\ const a: comptime_int = 1;
977 \\ var b: u32 = 0;
978 \\ switch (a) {
979 \\ 1 => b = 1,
980 \\ 2 => b = 2,
981 \\ else => unreachable,
982 \\ }
983 \\ return b;
984 \\}
985 \\
986 \\pub fn assert(ok: bool) void {
987 \\ if (!ok) unreachable; // assertion failure
988 \\}
989 \\
990 \\fn exit() noreturn {
991 \\ asm volatile ("syscall"
992 \\ :
993 \\ : [number] "{rax}" (231),
994 \\ [arg1] "{rdi}" (0)
995 \\ : "rcx", "r11", "memory"
996 \\ );
997 \\ unreachable;
998 \\}
999 ,
1000 "",
1001 );
1002 }965 }
1003966
1004 {967 {