authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-30 22:11:44-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-30 22:11:44-04:00
logef761c2cbc5c9a06da5c09de389a1d778731d170
tree5a3916c4bad25344395cde1913548f800f79aa05
parent6d999abba0fc4470f9bb2dae8997fded9bff60c4
parentca332f57f712c3b4570ca42bc503824022115142
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13360 from topolarity/comptime-bool-binops

Make `x and false` and `x or true` comptime-known

5 files changed, 120 insertions(+), 29 deletions(-)

src/Sema.zig+16-7
......@@ -15931,12 +15931,10 @@ fn zirBoolBr(
1593115931 const gpa = sema.gpa;
1593215932
1593315933 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
15934 if (lhs_val.toBool() == is_bool_or) {
15935 if (is_bool_or) {
15936 return Air.Inst.Ref.bool_true;
15937 } else {
15938 return Air.Inst.Ref.bool_false;
15939 }
15934 if (is_bool_or and lhs_val.toBool()) {
15935 return Air.Inst.Ref.bool_true;
15936 } else if (!is_bool_or and !lhs_val.toBool()) {
15937 return Air.Inst.Ref.bool_false;
1594015938 }
1594115939 // comptime-known left-hand side. No need for a block here; the result
1594215940 // is simply the rhs expression. Here we rely on there only being 1
......@@ -15976,7 +15974,18 @@ fn zirBoolBr(
1597615974 _ = try rhs_block.addBr(block_inst, rhs_result);
1597715975 }
1597815976
15979 return finishCondBr(sema, parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
15977 const result = finishCondBr(sema, parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
15978 if (!sema.typeOf(rhs_result).isNoReturn()) {
15979 if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| {
15980 if (is_bool_or and rhs_val.toBool()) {
15981 return Air.Inst.Ref.bool_true;
15982 } else if (!is_bool_or and !rhs_val.toBool()) {
15983 return Air.Inst.Ref.bool_false;
15984 }
15985 }
15986 }
15987
15988 return result;
1598015989}
1598115990
1598215991fn finishCondBr(
src/stage1/all_types.hpp+1
......@@ -2922,6 +2922,7 @@ struct Stage1ZirInstPhi {
29222922 Stage1ZirInst base;
29232923
29242924 size_t incoming_count;
2925 bool merge_comptime;
29252926 Stage1ZirBasicBlock **incoming_blocks;
29262927 Stage1ZirInst **incoming_values;
29272928 ResultLocPeerParent *peer_parent;
src/stage1/astgen.cpp+22-17
......@@ -1304,7 +1304,7 @@ static Stage1ZirInst *ir_build_call_src(Stage1AstGen *ag, Scope *scope, AstNode
13041304 return &call_instruction->base;
13051305}
13061306
1307static Stage1ZirInst *ir_build_phi(Stage1AstGen *ag, Scope *scope, AstNode *source_node,
1307static Stage1ZirInst *ir_build_phi(Stage1AstGen *ag, Scope *scope, AstNode *source_node, bool merge_comptime,
13081308 size_t incoming_count, Stage1ZirBasicBlock **incoming_blocks, Stage1ZirInst **incoming_values,
13091309 ResultLocPeerParent *peer_parent)
13101310{
......@@ -1316,6 +1316,7 @@ static Stage1ZirInst *ir_build_phi(Stage1AstGen *ag, Scope *scope, AstNode *sour
13161316 phi_instruction->incoming_blocks = incoming_blocks;
13171317 phi_instruction->incoming_values = incoming_values;
13181318 phi_instruction->peer_parent = peer_parent;
1319 phi_instruction->merge_comptime = merge_comptime;
13191320
13201321 for (size_t i = 0; i < incoming_count; i += 1) {
13211322 ir_ref_bb(incoming_blocks[i]);
......@@ -3393,7 +3394,7 @@ static Stage1ZirInst *astgen_block(Stage1AstGen *ag, Scope *parent_scope, AstNod
33933394 scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
33943395 }
33953396 ir_set_cursor_at_end_and_append_block(ag, scope_block->end_block);
3396 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, block_node, incoming_blocks.length,
3397 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, block_node, false, incoming_blocks.length,
33973398 incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
33983399 return ir_expr_wrap(ag, parent_scope, phi, result_loc);
33993400 } else {
......@@ -3423,7 +3424,7 @@ static Stage1ZirInst *astgen_block(Stage1AstGen *ag, Scope *parent_scope, AstNod
34233424 if (block_node->data.block.name != nullptr) {
34243425 ir_build_br(ag, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime);
34253426 ir_set_cursor_at_end_and_append_block(ag, scope_block->end_block);
3426 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, block_node, incoming_blocks.length,
3427 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, block_node, false, incoming_blocks.length,
34273428 incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
34283429 result = ir_expr_wrap(ag, parent_scope, phi, result_loc);
34293430 } else {
......@@ -3527,6 +3528,7 @@ static Stage1ZirInst *astgen_bool_or(Stage1AstGen *ag, Scope *scope, AstNode *no
35273528 // block for when val1 == true (don't even evaluate the second part)
35283529 Stage1ZirBasicBlock *true_block = ir_create_basic_block(ag, scope, "BoolOrTrue");
35293530
3531 Stage1ZirInst *val1_true = ir_build_const_bool(ag, scope, node, true);
35303532 ir_build_cond_br(ag, scope, node, val1, true_block, false_block, is_comptime);
35313533
35323534 ir_set_cursor_at_end_and_append_block(ag, false_block);
......@@ -3540,13 +3542,14 @@ static Stage1ZirInst *astgen_bool_or(Stage1AstGen *ag, Scope *scope, AstNode *no
35403542 ir_set_cursor_at_end_and_append_block(ag, true_block);
35413543
35423544 Stage1ZirInst **incoming_values = heap::c_allocator.allocate<Stage1ZirInst *>(2);
3543 incoming_values[0] = val1;
3545 incoming_values[0] = val1_true;
35443546 incoming_values[1] = val2;
35453547 Stage1ZirBasicBlock **incoming_blocks = heap::c_allocator.allocate<Stage1ZirBasicBlock *>(2);
35463548 incoming_blocks[0] = post_val1_block;
35473549 incoming_blocks[1] = post_val2_block;
35483550
3549 return ir_build_phi(ag, scope, node, 2, incoming_blocks, incoming_values, nullptr);
3551 const bool merge_comptime = true;
3552 return ir_build_phi(ag, scope, node, merge_comptime, 2, incoming_blocks, incoming_values, nullptr);
35503553}
35513554
35523555static Stage1ZirInst *astgen_bool_and(Stage1AstGen *ag, Scope *scope, AstNode *node) {
......@@ -3569,6 +3572,7 @@ static Stage1ZirInst *astgen_bool_and(Stage1AstGen *ag, Scope *scope, AstNode *n
35693572 // block for when val1 == false (don't even evaluate the second part)
35703573 Stage1ZirBasicBlock *false_block = ir_create_basic_block(ag, scope, "BoolAndFalse");
35713574
3575 Stage1ZirInst *val1_false = ir_build_const_bool(ag, scope, node, false);
35723576 ir_build_cond_br(ag, scope, node, val1, true_block, false_block, is_comptime);
35733577
35743578 ir_set_cursor_at_end_and_append_block(ag, true_block);
......@@ -3582,13 +3586,14 @@ static Stage1ZirInst *astgen_bool_and(Stage1AstGen *ag, Scope *scope, AstNode *n
35823586 ir_set_cursor_at_end_and_append_block(ag, false_block);
35833587
35843588 Stage1ZirInst **incoming_values = heap::c_allocator.allocate<Stage1ZirInst *>(2);
3585 incoming_values[0] = val1;
3589 incoming_values[0] = val1_false;
35863590 incoming_values[1] = val2;
35873591 Stage1ZirBasicBlock **incoming_blocks = heap::c_allocator.allocate<Stage1ZirBasicBlock *>(2);
35883592 incoming_blocks[0] = post_val1_block;
35893593 incoming_blocks[1] = post_val2_block;
35903594
3591 return ir_build_phi(ag, scope, node, 2, incoming_blocks, incoming_values, nullptr);
3595 const bool merge_comptime = true;
3596 return ir_build_phi(ag, scope, node, merge_comptime, 2, incoming_blocks, incoming_values, nullptr);
35923597}
35933598
35943599static ResultLocPeerParent *ir_build_result_peers(Stage1AstGen *ag, Stage1ZirInst *cond_br_inst,
......@@ -3678,7 +3683,7 @@ static Stage1ZirInst *astgen_orelse(Stage1AstGen *ag, Scope *parent_scope, AstNo
36783683 Stage1ZirBasicBlock **incoming_blocks = heap::c_allocator.allocate<Stage1ZirBasicBlock *>(2);
36793684 incoming_blocks[0] = after_null_block;
36803685 incoming_blocks[1] = after_ok_block;
3681 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
3686 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, node, false, 2, incoming_blocks, incoming_values, peer_parent);
36823687 return ir_lval_wrap(ag, parent_scope, phi, lval, result_loc);
36833688}
36843689
......@@ -5589,7 +5594,7 @@ static Stage1ZirInst *astgen_if_bool_expr(Stage1AstGen *ag, Scope *scope, AstNod
55895594 incoming_blocks[0] = after_then_block;
55905595 incoming_blocks[1] = after_else_block;
55915596
5592 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
5597 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, false, 2, incoming_blocks, incoming_values, peer_parent);
55935598 return ir_expr_wrap(ag, scope, phi, result_loc);
55945599}
55955600
......@@ -6224,7 +6229,7 @@ static Stage1ZirInst *astgen_while_expr(Stage1AstGen *ag, Scope *scope, AstNode
62246229 peer_parent->peers.last()->next_bb = end_block;
62256230 }
62266231
6227 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, incoming_blocks.length,
6232 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, false, incoming_blocks.length,
62286233 incoming_blocks.items, incoming_values.items, peer_parent);
62296234 return ir_expr_wrap(ag, scope, phi, result_loc);
62306235 } else if (var_symbol != nullptr) {
......@@ -6334,7 +6339,7 @@ static Stage1ZirInst *astgen_while_expr(Stage1AstGen *ag, Scope *scope, AstNode
63346339 peer_parent->peers.last()->next_bb = end_block;
63356340 }
63366341
6337 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, incoming_blocks.length,
6342 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, false, incoming_blocks.length,
63386343 incoming_blocks.items, incoming_values.items, peer_parent);
63396344 return ir_expr_wrap(ag, scope, phi, result_loc);
63406345 } else {
......@@ -6430,7 +6435,7 @@ static Stage1ZirInst *astgen_while_expr(Stage1AstGen *ag, Scope *scope, AstNode
64306435 peer_parent->peers.last()->next_bb = end_block;
64316436 }
64326437
6433 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, incoming_blocks.length,
6438 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, false, incoming_blocks.length,
64346439 incoming_blocks.items, incoming_values.items, peer_parent);
64356440 return ir_expr_wrap(ag, scope, phi, result_loc);
64366441 }
......@@ -6582,7 +6587,7 @@ static Stage1ZirInst *astgen_for_expr(Stage1AstGen *ag, Scope *parent_scope, Ast
65826587 peer_parent->peers.last()->next_bb = end_block;
65836588 }
65846589
6585 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, node, incoming_blocks.length,
6590 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, node, false, incoming_blocks.length,
65866591 incoming_blocks.items, incoming_values.items, peer_parent);
65876592 return ir_lval_wrap(ag, parent_scope, phi, lval, result_loc);
65886593}
......@@ -6910,7 +6915,7 @@ static Stage1ZirInst *astgen_if_optional_expr(Stage1AstGen *ag, Scope *scope, As
69106915 incoming_blocks[0] = after_then_block;
69116916 incoming_blocks[1] = after_else_block;
69126917
6913 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
6918 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, false, 2, incoming_blocks, incoming_values, peer_parent);
69146919 return ir_expr_wrap(ag, scope, phi, result_loc);
69156920}
69166921
......@@ -7008,7 +7013,7 @@ static Stage1ZirInst *astgen_if_err_expr(Stage1AstGen *ag, Scope *scope, AstNode
70087013 incoming_blocks[0] = after_then_block;
70097014 incoming_blocks[1] = after_else_block;
70107015
7011 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
7016 Stage1ZirInst *phi = ir_build_phi(ag, scope, node, false, 2, incoming_blocks, incoming_values, peer_parent);
70127017 return ir_expr_wrap(ag, scope, phi, result_loc);
70137018}
70147019
......@@ -7344,7 +7349,7 @@ static Stage1ZirInst *astgen_switch_expr(Stage1AstGen *ag, Scope *scope, AstNode
73447349 if (incoming_blocks.length == 0) {
73457350 result_instruction = ir_build_const_void(ag, scope, node);
73467351 } else {
7347 result_instruction = ir_build_phi(ag, scope, node, incoming_blocks.length,
7352 result_instruction = ir_build_phi(ag, scope, node, false, incoming_blocks.length,
73487353 incoming_blocks.items, incoming_values.items, peer_parent);
73497354 }
73507355 return ir_lval_wrap(ag, scope, result_instruction, lval, result_loc);
......@@ -7671,7 +7676,7 @@ static Stage1ZirInst *astgen_catch(Stage1AstGen *ag, Scope *parent_scope, AstNod
76717676 Stage1ZirBasicBlock **incoming_blocks = heap::c_allocator.allocate<Stage1ZirBasicBlock *>(2);
76727677 incoming_blocks[0] = after_err_block;
76737678 incoming_blocks[1] = after_ok_block;
7674 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
7679 Stage1ZirInst *phi = ir_build_phi(ag, parent_scope, node, false, 2, incoming_blocks, incoming_values, peer_parent);
76757680 return ir_lval_wrap(ag, parent_scope, phi, lval, result_loc);
76767681}
76777682
src/stage1/ir.cpp+31-5
......@@ -1318,12 +1318,37 @@ static Stage1AirInstCall *ir_build_call_gen(IrAnalyze *ira, Scope *scope, AstNod
13181318 return call_instruction;
13191319}
13201320
1321static Stage1AirInst *ir_build_phi_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, size_t incoming_count,
1322 Stage1AirBasicBlock **incoming_blocks, Stage1AirInst **incoming_values, ZigType *result_type)
1321static Stage1AirInst *ir_build_phi_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, bool merge_comptime,
1322 size_t incoming_count, Stage1AirBasicBlock **incoming_blocks, Stage1AirInst **incoming_values, ZigType *result_type)
13231323{
13241324 assert(incoming_count != 0);
13251325 assert(incoming_count != SIZE_MAX);
13261326
1327 if (merge_comptime && instr_is_comptime(incoming_values[incoming_count - 1])) {
1328 // We need to check whether all the merged values are comptime-known and equal.
1329 // If so, we elide the runtime phi and replace it with any of the identical comptime-known values.
1330 ZigValue *comptime_value = ir_resolve_const(ira, incoming_values[incoming_count - 1], UndefOk);
1331 if (comptime_value == nullptr)
1332 return ira->codegen->invalid_inst_gen;
1333
1334 for (size_t i = incoming_count - 1; i > 0;) {
1335 i -= 1;
1336 if (!instr_is_comptime(incoming_values[i])) {
1337 comptime_value = nullptr;
1338 break;
1339 }
1340 ZigValue *value = ir_resolve_const(ira, incoming_values[i], UndefOk);
1341 if (value == nullptr)
1342 return ira->codegen->invalid_inst_gen;
1343 if (!const_values_equal(ira->codegen, comptime_value, value)) {
1344 comptime_value = nullptr;
1345 break;
1346 }
1347 }
1348 if (comptime_value != nullptr)
1349 return incoming_values[0];
1350 }
1351
13271352 Stage1AirInstPhi *phi_instruction = ir_build_inst_gen<Stage1AirInstPhi>(&ira->new_irb,
13281353 scope, source_node);
13291354 phi_instruction->base.value->type = result_type;
......@@ -9592,7 +9617,8 @@ static Stage1AirInst *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, Scop
95929617 incoming_values[0] = null_result;
95939618 incoming_values[1] = non_null_cmp_result;
95949619
9595 return ir_build_phi_gen(ira, scope, source_node, incoming_count, incoming_blocks, incoming_values, result_type);
9620 const bool merge_comptime = false;
9621 return ir_build_phi_gen(ira, scope, source_node, merge_comptime, incoming_count, incoming_blocks, incoming_values, result_type);
95969622}
95979623
95989624static Stage1AirInst *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, Scope *scope, AstNode *source_node,
......@@ -14757,8 +14783,8 @@ static Stage1AirInst *ir_analyze_instruction_phi(IrAnalyze *ira, Stage1ZirInstPh
1475714783 ir_set_cursor_at_end_gen(&ira->new_irb, cur_bb);
1475814784
1475914785 Stage1AirInst *result = ir_build_phi_gen(ira, phi_instruction->base.scope,
14760 phi_instruction->base.source_node, new_incoming_blocks.length,
14761 new_incoming_blocks.items, new_incoming_values.items, resolved_type);
14786 phi_instruction->base.source_node, phi_instruction->merge_comptime,
14787 new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items, resolved_type);
1476214788
1476314789 if (all_stack_ptrs) {
1476414790 assert(result->value->special == ConstValSpecialRuntime);
test/behavior/eval.zig+50
......@@ -1438,3 +1438,53 @@ test "continue nested inline for loop in named block expr" {
14381438 }
14391439 try expect(a == 2);
14401440}
1441
1442test "x and false is comptime-known false" {
1443 const T = struct {
1444 var x: u32 = 0;
1445
1446 fn foo() bool {
1447 x += 1; // Observable side-effect
1448 return true;
1449 }
1450 };
1451
1452 if (T.foo() and T.foo() and false and T.foo()) {
1453 @compileError("Condition should be comptime-known false");
1454 }
1455 try expect(T.x == 2);
1456
1457 T.x = 0;
1458 if (T.foo() and T.foo() and b: {
1459 _ = T.foo();
1460 break :b false;
1461 } and T.foo()) {
1462 @compileError("Condition should be comptime-known false");
1463 }
1464 try expect(T.x == 3);
1465}
1466
1467test "x or true is comptime-known true" {
1468 const T = struct {
1469 var x: u32 = 0;
1470
1471 fn foo() bool {
1472 x += 1; // Observable side-effect
1473 return false;
1474 }
1475 };
1476
1477 if (!(T.foo() or T.foo() or true or T.foo())) {
1478 @compileError("Condition should be comptime-known false");
1479 }
1480 try expect(T.x == 2);
1481
1482 T.x = 0;
1483 if (!(T.foo() or T.foo() or b: {
1484 _ = T.foo();
1485 break :b true;
1486 } or T.foo())) {
1487 @compileError("Condition should be comptime-known false");
1488 }
1489 try expect(T.x == 3);
1490}