From 583f5aa50894b7a66a219b7427922bccf64148f5 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 1 Apr 2024 13:03:05 -0700 Subject: [PATCH] Constant evaluation support for `if` expressions, `and`, and `or`. (#3840) These can't be supported by `TryEvalInst`, because we don't track sufficient information about predecessors and branch conditions in SemIR to efficiently compute the constant result. Even if we could do so, we may not want to treat all `BlockArg`s for which we can infer a constant value as being constants. Instead, set the constant value explicitly after creating the corresponding `BlockArg` instruction. --- toolchain/check/context.cpp | 29 +++ toolchain/check/context.h | 11 ++ toolchain/check/handle_if_expr.cpp | 6 +- toolchain/check/handle_operator.cpp | 14 +- .../testdata/alias/fail_control_flow.carbon | 2 +- .../if_expr/constant_condition.carbon | 108 ++++++++++ .../if_expr/fail_not_in_function.carbon | 22 +-- .../if_expr/fail_partial_constant.carbon | 128 ++++++++++++ .../testdata/operators/builtin/and.carbon | 168 ++++++++++++++++ .../fail_and_or_not_in_function.carbon | 2 +- .../fail_and_or_partial_constant.carbon | 185 ++++++++++++++++++ .../fail_assignment_to_non_assignable.carbon | 2 +- .../testdata/operators/builtin/or.carbon | 168 ++++++++++++++++ .../operators/builtin/unary_op.carbon | 52 +++++ .../pointer/fail_address_of_value.carbon | 2 +- 15 files changed, 879 insertions(+), 20 deletions(-) create mode 100644 toolchain/check/testdata/if_expr/fail_partial_constant.carbon create mode 100644 toolchain/check/testdata/operators/builtin/fail_and_or_partial_constant.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 1e426644d0aa..4b6b69872216 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -479,6 +479,35 @@ auto Context::AddConvergenceBlockWithArgAndPush( return AddInst({node_id, SemIR::BlockArg{result_type_id, new_block_id}}); } +auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id, + SemIR::InstId cond_id, + SemIR::InstId if_true, + SemIR::InstId if_false) + -> void { + CARBON_CHECK(insts().Is(select_id)); + + // Determine the constant result based on the condition value. + SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant; + auto cond_const_id = constant_values().Get(cond_id); + if (!cond_const_id.is_template()) { + // Symbolic or non-constant condition means a non-constant result. + } else if (auto literal = insts().TryGetAs( + cond_const_id.inst_id())) { + const_id = constant_values().Get(literal.value().value.ToBool() ? if_true + : if_false); + } else { + CARBON_CHECK(cond_const_id == SemIR::ConstantId::Error) + << "Unexpected constant branch condition."; + const_id = SemIR::ConstantId::Error; + } + + if (const_id.is_constant()) { + CARBON_VLOG() << "Constant: " << insts().Get(select_id) << " -> " + << const_id.inst_id() << "\n"; + constant_values().Set(select_id, const_id); + } +} + // Add the current code block to the enclosing function. auto Context::AddCurrentCodeBlockToFunction(Parse::NodeId node_id) -> void { CARBON_CHECK(!inst_block_stack().empty()) << "no current code block"; diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 62f5d4baddcd..2633075a53f4 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -177,6 +177,17 @@ class Context { Parse::NodeId node_id, std::initializer_list block_args) -> SemIR::InstId; + // Sets the constant value of a block argument created as the result of a + // branch. `select_id` should be a `BlockArg` that selects between two + // values. `cond_id` is the condition, `if_false` is the value to use if the + // condition is false, and `if_true` is the value to use if the condition is + // true. We don't track enough information in the `BlockArg` inst for + // `TryEvalInst` to do this itself. + auto SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id, + SemIR::InstId cond_id, + SemIR::InstId if_true, + SemIR::InstId if_false) -> void; + // Add the current code block to the enclosing function. // TODO: The node_id is taken for expressions, which can occur in // non-function contexts. This should be refactored to support non-function diff --git a/toolchain/check/handle_if_expr.cpp b/toolchain/check/handle_if_expr.cpp index 0c5cdbe9c4ce..1d9177bd2b6c 100644 --- a/toolchain/check/handle_if_expr.cpp +++ b/toolchain/check/handle_if_expr.cpp @@ -11,10 +11,11 @@ auto HandleIfExprIf(Context& context, Parse::IfExprIfId node_id) -> bool { // Alias node_id for if/then/else consistency. auto& if_node = node_id; - auto cond_value_id = context.node_stack().PopExpr(); + auto [cond_node, cond_value_id] = context.node_stack().PopExprWithNodeId(); // Convert the condition to `bool`, and branch on it. cond_value_id = ConvertToBoolValue(context, if_node, cond_value_id); + context.node_stack().Push(cond_node, cond_value_id); auto then_block_id = context.AddDominatedBlockAndBranchIf(if_node, cond_value_id); auto else_block_id = context.AddDominatedBlockAndBranch(if_node); @@ -51,6 +52,7 @@ auto HandleIfExprElse(Context& context, Parse::IfExprElseId node_id) -> bool { auto then_value_id = context.node_stack().Pop(); auto [if_node, _] = context.node_stack().PopWithNodeId(); + auto cond_value_id = context.node_stack().PopExpr(); // Convert the `else` value to the `then` value's type, and finish the `else` // block. @@ -62,6 +64,8 @@ auto HandleIfExprElse(Context& context, Parse::IfExprElseId node_id) -> bool { // Create a resumption block and branches to it. auto chosen_value_id = context.AddConvergenceBlockWithArgAndPush( if_node, {else_value_id, then_value_id}); + context.SetBlockArgResultBeforeConstantUse(chosen_value_id, cond_value_id, + then_value_id, else_value_id); context.AddCurrentCodeBlockToFunction(node_id); // Push the result value. diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index 9ee205507632..7d5248533805 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -343,7 +343,7 @@ auto HandlePrefixOperatorStar(Context& context, static auto HandleShortCircuitOperand(Context& context, Parse::NodeId node_id, bool is_or) -> bool { // Convert the condition to `bool`. - auto cond_value_id = context.node_stack().PopExpr(); + auto [cond_node, cond_value_id] = context.node_stack().PopExprWithNodeId(); cond_value_id = ConvertToBoolValue(context, node_id, cond_value_id); auto bool_type_id = context.insts().Get(cond_value_id).type_id(); @@ -362,6 +362,11 @@ static auto HandleShortCircuitOperand(Context& context, Parse::NodeId node_id, auto end_block_id = context.AddDominatedBlockAndBranchWithArg( node_id, short_circuit_result_id); + // Push the branch condition and result for use when handling the complete + // expression. + context.node_stack().Push(cond_node, branch_value_id); + context.node_stack().Push(cond_node, short_circuit_result_id); + // Push the resumption and the right-hand side blocks, and start emitting the // right-hand operand. context.inst_block_stack().Pop(); @@ -391,6 +396,8 @@ auto HandleShortCircuitOperandOr(Context& context, static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id) -> bool { auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); + auto short_circuit_result_id = context.node_stack().PopExpr(); + auto branch_value_id = context.node_stack().PopExpr(); // The first operand is wrapped in a ShortCircuitOperand, which we // already handled by creating a RHS block and a resumption block, which @@ -405,9 +412,12 @@ static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id) context.AddCurrentCodeBlockToFunction(node_id); // Collect the result from either the first or second operand. - context.AddInstAndPush( + auto result_id = context.AddInst( {node_id, SemIR::BlockArg{context.insts().Get(rhs_id).type_id(), resume_block_id}}); + context.SetBlockArgResultBeforeConstantUse(result_id, branch_value_id, rhs_id, + short_circuit_result_id); + context.node_stack().Push(node_id, result_id); return true; } diff --git a/toolchain/check/testdata/alias/fail_control_flow.carbon b/toolchain/check/testdata/alias/fail_control_flow.carbon index 9f1cbe76a1e0..2478d11a8352 100644 --- a/toolchain/check/testdata/alias/fail_control_flow.carbon +++ b/toolchain/check/testdata/alias/fail_control_flow.carbon @@ -25,7 +25,7 @@ alias a = true or false; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: %.loc18: bool = block_arg +// CHECK:STDOUT: %.loc18: bool = block_arg [template = constants.%.1] // CHECK:STDOUT: %a: = bind_alias a, [template = ] // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/if_expr/constant_condition.carbon b/toolchain/check/testdata/if_expr/constant_condition.carbon index c8bbb4d44529..31ae362a4b32 100644 --- a/toolchain/check/testdata/if_expr/constant_condition.carbon +++ b/toolchain/check/testdata/if_expr/constant_condition.carbon @@ -15,6 +15,18 @@ fn G() -> i32 { return if false then A() else B(); } +fn Constant() -> i32 { + var v: if true then i32 else i32* = 1; + var w: if false then i32 else i32* = &v; + return *w; +} + +fn PartiallyConstant(t: type) -> i32 { + var v: if true then i32 else t = 1; + var w: if false then t else i32* = &v; + return *w; +} + // CHECK:STDOUT: --- constant_condition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -22,6 +34,7 @@ fn G() -> i32 { // CHECK:STDOUT: %.2: i32 = int_literal 2 [template] // CHECK:STDOUT: %.3: bool = bool_literal true [template] // CHECK:STDOUT: %.4: bool = bool_literal false [template] +// CHECK:STDOUT: %.5: type = ptr_type i32 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -30,6 +43,8 @@ fn G() -> i32 { // CHECK:STDOUT: .B = %B // CHECK:STDOUT: .F = %F // CHECK:STDOUT: .G = %G +// CHECK:STDOUT: .Constant = %Constant +// CHECK:STDOUT: .PartiallyConstant = %PartiallyConstant // CHECK:STDOUT: } // CHECK:STDOUT: %A: = fn_decl @A [template] { // CHECK:STDOUT: %return.var.loc7: ref i32 = var @@ -43,6 +58,14 @@ fn G() -> i32 { // CHECK:STDOUT: %G: = fn_decl @G [template] { // CHECK:STDOUT: %return.var.loc14: ref i32 = var // CHECK:STDOUT: } +// CHECK:STDOUT: %Constant: = fn_decl @Constant [template] { +// CHECK:STDOUT: %return.var.loc18: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %PartiallyConstant: = fn_decl @PartiallyConstant [template] { +// CHECK:STDOUT: %t.loc24_22.1: type = param t +// CHECK:STDOUT: @PartiallyConstant.%t: type = bind_name t, %t.loc24_22.1 +// CHECK:STDOUT: %return.var.loc24: ref i32 = var +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() -> i32 { @@ -105,3 +128,88 @@ fn G() -> i32 { // CHECK:STDOUT: return %.loc15_10 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @Constant() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc19_13: bool = bool_literal true [template = constants.%.3] +// CHECK:STDOUT: if %.loc19_13 br !if.expr.then.loc19 else br !if.expr.else.loc19 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc19: +// CHECK:STDOUT: br !if.expr.result.loc19(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc19: +// CHECK:STDOUT: %.loc19_35: type = ptr_type i32 [template = constants.%.5] +// CHECK:STDOUT: br !if.expr.result.loc19(%.loc19_35) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc19: +// CHECK:STDOUT: %.loc19_10: type = block_arg !if.expr.result.loc19 [template = i32] +// CHECK:STDOUT: %v.var: ref i32 = var v +// CHECK:STDOUT: %v: ref i32 = bind_name v, %v.var +// CHECK:STDOUT: %.loc19_39: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: assign %v.var, %.loc19_39 +// CHECK:STDOUT: %.loc20_13: bool = bool_literal false [template = constants.%.4] +// CHECK:STDOUT: if %.loc20_13 br !if.expr.then.loc20 else br !if.expr.else.loc20 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc20: +// CHECK:STDOUT: br !if.expr.result.loc20(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc20: +// CHECK:STDOUT: %.loc20_36: type = ptr_type i32 [template = constants.%.5] +// CHECK:STDOUT: br !if.expr.result.loc20(%.loc20_36) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc20: +// CHECK:STDOUT: %.loc20_10: type = block_arg !if.expr.result.loc20 [template = constants.%.5] +// CHECK:STDOUT: %w.var: ref i32* = var w +// CHECK:STDOUT: %w: ref i32* = bind_name w, %w.var +// CHECK:STDOUT: %v.ref: ref i32 = name_ref v, %v +// CHECK:STDOUT: %.loc20_40: i32* = addr_of %v.ref +// CHECK:STDOUT: assign %w.var, %.loc20_40 +// CHECK:STDOUT: %w.ref: ref i32* = name_ref w, %w +// CHECK:STDOUT: %.loc21_11: i32* = bind_value %w.ref +// CHECK:STDOUT: %.loc21_10.1: ref i32 = deref %.loc21_11 +// CHECK:STDOUT: %.loc21_10.2: i32 = bind_value %.loc21_10.1 +// CHECK:STDOUT: return %.loc21_10.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @PartiallyConstant(%t: type) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc25_13: bool = bool_literal true [template = constants.%.3] +// CHECK:STDOUT: if %.loc25_13 br !if.expr.then.loc25 else br !if.expr.else.loc25 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc25: +// CHECK:STDOUT: br !if.expr.result.loc25(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc25: +// CHECK:STDOUT: %t.ref.loc25: type = name_ref t, %t +// CHECK:STDOUT: br !if.expr.result.loc25(%t.ref.loc25) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc25: +// CHECK:STDOUT: %.loc25_10: type = block_arg !if.expr.result.loc25 [template = i32] +// CHECK:STDOUT: %v.var: ref i32 = var v +// CHECK:STDOUT: %v: ref i32 = bind_name v, %v.var +// CHECK:STDOUT: %.loc25_36: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: assign %v.var, %.loc25_36 +// CHECK:STDOUT: %.loc26_13: bool = bool_literal false [template = constants.%.4] +// CHECK:STDOUT: if %.loc26_13 br !if.expr.then.loc26 else br !if.expr.else.loc26 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc26: +// CHECK:STDOUT: %t.ref.loc26: type = name_ref t, %t +// CHECK:STDOUT: br !if.expr.result.loc26(%t.ref.loc26) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc26: +// CHECK:STDOUT: %.loc26_34: type = ptr_type i32 [template = constants.%.5] +// CHECK:STDOUT: br !if.expr.result.loc26(%.loc26_34) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc26: +// CHECK:STDOUT: %.loc26_10: type = block_arg !if.expr.result.loc26 [template = constants.%.5] +// CHECK:STDOUT: %w.var: ref i32* = var w +// CHECK:STDOUT: %w: ref i32* = bind_name w, %w.var +// CHECK:STDOUT: %v.ref: ref i32 = name_ref v, %v +// CHECK:STDOUT: %.loc26_38: i32* = addr_of %v.ref +// CHECK:STDOUT: assign %w.var, %.loc26_38 +// CHECK:STDOUT: %w.ref: ref i32* = name_ref w, %w +// CHECK:STDOUT: %.loc27_11: i32* = bind_value %w.ref +// CHECK:STDOUT: %.loc27_10.1: ref i32 = deref %.loc27_11 +// CHECK:STDOUT: %.loc27_10.2: i32 = bind_value %.loc27_10.1 +// CHECK:STDOUT: return %.loc27_10.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/if_expr/fail_not_in_function.carbon b/toolchain/check/testdata/if_expr/fail_not_in_function.carbon index 54042af028bc..bd5c08fc7160 100644 --- a/toolchain/check/testdata/if_expr/fail_not_in_function.carbon +++ b/toolchain/check/testdata/if_expr/fail_not_in_function.carbon @@ -4,7 +4,6 @@ // // AUTOUPDATE -// TODO: Should work with compile-time evaluation. // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+12]]:14: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~~ @@ -20,16 +19,11 @@ let x: i32 = if true then 1 else 0; class C { - // TODO: Should work with compile-time evaluation. - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+15]]:10: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. + // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+11]]:10: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+11]]:10: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. - // CHECK:STDERR: var n: if true then i32 else f64; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+7]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+7]]:10: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -46,20 +40,22 @@ class C { // CHECK:STDOUT: %.2: i32 = int_literal 1 [template] // CHECK:STDOUT: %.3: i32 = int_literal 0 [template] // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.4: type = unbound_element_type C, i32 [template] +// CHECK:STDOUT: %.5: type = struct_type {.n: i32} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: %.loc20: i32 = block_arg -// CHECK:STDOUT: %x: i32 = bind_name x, %.loc20 +// CHECK:STDOUT: %.loc19: i32 = block_arg [template = constants.%.2] +// CHECK:STDOUT: %x: i32 = bind_name x, %.loc19 // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: %.loc39: bool = bool_literal true [template = constants.%.1] -// CHECK:STDOUT: if %.loc39 br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: %.loc33: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc33 br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .n = +// CHECK:STDOUT: .n = // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/if_expr/fail_partial_constant.carbon b/toolchain/check/testdata/if_expr/fail_partial_constant.carbon new file mode 100644 index 000000000000..e32a7c61e6f0 --- /dev/null +++ b/toolchain/check/testdata/if_expr/fail_partial_constant.carbon @@ -0,0 +1,128 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_non_constant_condition.carbon + +package NonConstantCondition api; + +fn ConditionIsNonConstant(b: bool) { + // We choose to not accept this even if both arms evaluate to the same + // constant value, because it notionally involves evaluating a non-constant + // condition. + // CHECK:STDERR: fail_non_constant_condition.carbon:[[@LINE+4]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var v: if b then i32 else i32 = 1; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var v: if b then i32 else i32 = 1; +} + +// --- fail_non_constant_result.carbon + +package NonConstantResult api; + +fn ChosenBranchIsNonConstant(t: type) { + // CHECK:STDERR: fail_non_constant_result.carbon:[[@LINE+4]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var v: if true then t else i32 = 1; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var v: if true then t else i32 = 1; + // CHECK:STDERR: fail_non_constant_result.carbon:[[@LINE+3]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var w: if false then i32 else t = 1; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ + var w: if false then i32 else t = 1; +} + +// CHECK:STDOUT: --- fail_non_constant_condition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .ConditionIsNonConstant = %ConditionIsNonConstant +// CHECK:STDOUT: } +// CHECK:STDOUT: %ConditionIsNonConstant: = fn_decl @ConditionIsNonConstant [template] { +// CHECK:STDOUT: %b.loc4_27.1: bool = param b +// CHECK:STDOUT: @ConditionIsNonConstant.%b: bool = bind_name b, %b.loc4_27.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @ConditionIsNonConstant(%b: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.ref: bool = name_ref b, %b +// CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then: +// CHECK:STDOUT: br !if.expr.result(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else: +// CHECK:STDOUT: br !if.expr.result(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result: +// CHECK:STDOUT: %.loc12_10: type = block_arg !if.expr.result +// CHECK:STDOUT: %v.var: ref = var v +// CHECK:STDOUT: %v: ref = bind_name v, %v.var +// CHECK:STDOUT: %.loc12_35: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: assign %v.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_non_constant_result.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal true [template] +// CHECK:STDOUT: %.2: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.3: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .ChosenBranchIsNonConstant = %ChosenBranchIsNonConstant +// CHECK:STDOUT: } +// CHECK:STDOUT: %ChosenBranchIsNonConstant: = fn_decl @ChosenBranchIsNonConstant [template] { +// CHECK:STDOUT: %t.loc4_30.1: type = param t +// CHECK:STDOUT: @ChosenBranchIsNonConstant.%t: type = bind_name t, %t.loc4_30.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @ChosenBranchIsNonConstant(%t: type) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc9_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc9_13 br !if.expr.then.loc9 else br !if.expr.else.loc9 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc9: +// CHECK:STDOUT: %t.ref.loc9: type = name_ref t, %t +// CHECK:STDOUT: br !if.expr.result.loc9(%t.ref.loc9) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc9: +// CHECK:STDOUT: br !if.expr.result.loc9(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc9: +// CHECK:STDOUT: %.loc9_10: type = block_arg !if.expr.result.loc9 +// CHECK:STDOUT: %v.var: ref = var v +// CHECK:STDOUT: %v: ref = bind_name v, %v.var +// CHECK:STDOUT: %.loc9_36: i32 = int_literal 1 [template = constants.%.2] +// CHECK:STDOUT: assign %v.var, +// CHECK:STDOUT: %.loc13_13: bool = bool_literal false [template = constants.%.3] +// CHECK:STDOUT: if %.loc13_13 br !if.expr.then.loc13 else br !if.expr.else.loc13 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc13: +// CHECK:STDOUT: br !if.expr.result.loc13(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc13: +// CHECK:STDOUT: %t.ref.loc13: type = name_ref t, %t +// CHECK:STDOUT: br !if.expr.result.loc13(%t.ref.loc13) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc13: +// CHECK:STDOUT: %.loc13_10: type = block_arg !if.expr.result.loc13 +// CHECK:STDOUT: %w.var: ref = var w +// CHECK:STDOUT: %w: ref = bind_name w, %w.var +// CHECK:STDOUT: %.loc13_37: i32 = int_literal 1 [template = constants.%.2] +// CHECK:STDOUT: assign %w.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/builtin/and.carbon b/toolchain/check/testdata/operators/builtin/and.carbon index bfc95129f2d7..499a89d5d894 100644 --- a/toolchain/check/testdata/operators/builtin/and.carbon +++ b/toolchain/check/testdata/operators/builtin/and.carbon @@ -11,11 +11,24 @@ fn And() -> bool { return F() and G(); } +fn Constant() { + var a: if true and true then bool else () = true; + var b: if true and false then bool else () = (); + var c: if false and true then bool else () = (); + var d: if false and false then bool else () = (); +} + +fn PartialConstant(x: bool) { + var a: if false and x then bool else () = (); +} + // CHECK:STDOUT: --- and.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: bool = bool_literal true [template] // CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %.4: () = tuple_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -23,6 +36,8 @@ fn And() -> bool { // CHECK:STDOUT: .F = %F // CHECK:STDOUT: .G = %G // CHECK:STDOUT: .And = %And +// CHECK:STDOUT: .Constant = %Constant +// CHECK:STDOUT: .PartialConstant = %PartialConstant // CHECK:STDOUT: } // CHECK:STDOUT: %F: = fn_decl @F [template] { // CHECK:STDOUT: %return.var.loc7: ref bool = var @@ -33,6 +48,11 @@ fn And() -> bool { // CHECK:STDOUT: %And: = fn_decl @And [template] { // CHECK:STDOUT: %return.var.loc10: ref bool = var // CHECK:STDOUT: } +// CHECK:STDOUT: %Constant: = fn_decl @Constant [template] {} +// CHECK:STDOUT: %PartialConstant: = fn_decl @PartialConstant [template] { +// CHECK:STDOUT: %x.loc21_20.1: bool = param x +// CHECK:STDOUT: @PartialConstant.%x: bool = bind_name x, %x.loc21_20.1 +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> bool { @@ -68,3 +88,151 @@ fn And() -> bool { // CHECK:STDOUT: return %.loc11_14.4 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @Constant() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc15_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc15_18.1: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: if %.loc15_13 br !and.rhs.loc15 else br !and.result.loc15(%.loc15_18.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs.loc15: +// CHECK:STDOUT: %.loc15_22: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: br !and.result.loc15(%.loc15_22) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result.loc15: +// CHECK:STDOUT: %.loc15_18.2: bool = block_arg !and.result.loc15 [template = constants.%.1] +// CHECK:STDOUT: if %.loc15_18.2 br !if.expr.then.loc15 else br !if.expr.else.loc15 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc15: +// CHECK:STDOUT: br !if.expr.result.loc15(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc15: +// CHECK:STDOUT: %.loc15_43.1: () = tuple_literal () +// CHECK:STDOUT: %.loc15_43.2: type = converted %.loc15_43.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc15(%.loc15_43.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc15: +// CHECK:STDOUT: %.loc15_10: type = block_arg !if.expr.result.loc15 [template = bool] +// CHECK:STDOUT: %a.var: ref bool = var a +// CHECK:STDOUT: %a: ref bool = bind_name a, %a.var +// CHECK:STDOUT: %.loc15_47: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: assign %a.var, %.loc15_47 +// CHECK:STDOUT: %.loc16_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc16_18.1: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: if %.loc16_13 br !and.rhs.loc16 else br !and.result.loc16(%.loc16_18.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs.loc16: +// CHECK:STDOUT: %.loc16_22: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: br !and.result.loc16(%.loc16_22) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result.loc16: +// CHECK:STDOUT: %.loc16_18.2: bool = block_arg !and.result.loc16 [template = constants.%.2] +// CHECK:STDOUT: if %.loc16_18.2 br !if.expr.then.loc16 else br !if.expr.else.loc16 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc16: +// CHECK:STDOUT: br !if.expr.result.loc16(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc16: +// CHECK:STDOUT: %.loc16_44.1: () = tuple_literal () +// CHECK:STDOUT: %.loc16_44.2: type = converted %.loc16_44.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc16(%.loc16_44.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc16: +// CHECK:STDOUT: %.loc16_10: type = block_arg !if.expr.result.loc16 [template = constants.%.3] +// CHECK:STDOUT: %b.var: ref () = var b +// CHECK:STDOUT: %b: ref () = bind_name b, %b.var +// CHECK:STDOUT: %.loc16_49.1: () = tuple_literal () +// CHECK:STDOUT: %.loc16_49.2: init () = tuple_init () to %b.var [template = constants.%.4] +// CHECK:STDOUT: %.loc16_49.3: init () = converted %.loc16_49.1, %.loc16_49.2 [template = constants.%.4] +// CHECK:STDOUT: assign %b.var, %.loc16_49.3 +// CHECK:STDOUT: %.loc17_13: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc17_19.1: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: if %.loc17_13 br !and.rhs.loc17 else br !and.result.loc17(%.loc17_19.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs.loc17: +// CHECK:STDOUT: %.loc17_23: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: br !and.result.loc17(%.loc17_23) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result.loc17: +// CHECK:STDOUT: %.loc17_19.2: bool = block_arg !and.result.loc17 [template = constants.%.2] +// CHECK:STDOUT: if %.loc17_19.2 br !if.expr.then.loc17 else br !if.expr.else.loc17 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc17: +// CHECK:STDOUT: br !if.expr.result.loc17(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc17: +// CHECK:STDOUT: %.loc17_44.1: () = tuple_literal () +// CHECK:STDOUT: %.loc17_44.2: type = converted %.loc17_44.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc17(%.loc17_44.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc17: +// CHECK:STDOUT: %.loc17_10: type = block_arg !if.expr.result.loc17 [template = constants.%.3] +// CHECK:STDOUT: %c.var: ref () = var c +// CHECK:STDOUT: %c: ref () = bind_name c, %c.var +// CHECK:STDOUT: %.loc17_49.1: () = tuple_literal () +// CHECK:STDOUT: %.loc17_49.2: init () = tuple_init () to %c.var [template = constants.%.4] +// CHECK:STDOUT: %.loc17_49.3: init () = converted %.loc17_49.1, %.loc17_49.2 [template = constants.%.4] +// CHECK:STDOUT: assign %c.var, %.loc17_49.3 +// CHECK:STDOUT: %.loc18_13: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc18_19.1: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: if %.loc18_13 br !and.rhs.loc18 else br !and.result.loc18(%.loc18_19.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs.loc18: +// CHECK:STDOUT: %.loc18_23: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: br !and.result.loc18(%.loc18_23) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result.loc18: +// CHECK:STDOUT: %.loc18_19.2: bool = block_arg !and.result.loc18 [template = constants.%.2] +// CHECK:STDOUT: if %.loc18_19.2 br !if.expr.then.loc18 else br !if.expr.else.loc18 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc18: +// CHECK:STDOUT: br !if.expr.result.loc18(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc18: +// CHECK:STDOUT: %.loc18_45.1: () = tuple_literal () +// CHECK:STDOUT: %.loc18_45.2: type = converted %.loc18_45.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc18(%.loc18_45.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc18: +// CHECK:STDOUT: %.loc18_10: type = block_arg !if.expr.result.loc18 [template = constants.%.3] +// CHECK:STDOUT: %d.var: ref () = var d +// CHECK:STDOUT: %d: ref () = bind_name d, %d.var +// CHECK:STDOUT: %.loc18_50.1: () = tuple_literal () +// CHECK:STDOUT: %.loc18_50.2: init () = tuple_init () to %d.var [template = constants.%.4] +// CHECK:STDOUT: %.loc18_50.3: init () = converted %.loc18_50.1, %.loc18_50.2 [template = constants.%.4] +// CHECK:STDOUT: assign %d.var, %.loc18_50.3 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @PartialConstant(%x: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc22_13: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc22_19.1: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: if %.loc22_13 br !and.rhs else br !and.result(%.loc22_19.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs: +// CHECK:STDOUT: %x.ref: bool = name_ref x, %x +// CHECK:STDOUT: br !and.result(%x.ref) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result: +// CHECK:STDOUT: %.loc22_19.2: bool = block_arg !and.result [template = constants.%.2] +// CHECK:STDOUT: if %.loc22_19.2 br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then: +// CHECK:STDOUT: br !if.expr.result(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else: +// CHECK:STDOUT: %.loc22_41.1: () = tuple_literal () +// CHECK:STDOUT: %.loc22_41.2: type = converted %.loc22_41.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result(%.loc22_41.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result: +// CHECK:STDOUT: %.loc22_10: type = block_arg !if.expr.result [template = constants.%.3] +// CHECK:STDOUT: %a.var: ref () = var a +// CHECK:STDOUT: %a: ref () = bind_name a, %a.var +// CHECK:STDOUT: %.loc22_46.1: () = tuple_literal () +// CHECK:STDOUT: %.loc22_46.2: init () = tuple_init () to %a.var [template = constants.%.4] +// CHECK:STDOUT: %.loc22_46.3: init () = converted %.loc22_46.1, %.loc22_46.2 [template = constants.%.4] +// CHECK:STDOUT: assign %a.var, %.loc22_46.3 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon b/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon index b256eab0f039..62bc515e677e 100644 --- a/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon +++ b/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon @@ -45,7 +45,7 @@ var or_: F(true or true); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: %.loc38_17: bool = block_arg +// CHECK:STDOUT: %.loc38_17: bool = block_arg [template = constants.%.1] // CHECK:STDOUT: %.loc38_11.1: init type = call (%.loc38_17) // CHECK:STDOUT: %.loc38_24: type = value_of_initializer %.loc38_11.1 // CHECK:STDOUT: %.loc38_11.2: type = converted %.loc38_11.1, %.loc38_24 diff --git a/toolchain/check/testdata/operators/builtin/fail_and_or_partial_constant.carbon b/toolchain/check/testdata/operators/builtin/fail_and_or_partial_constant.carbon new file mode 100644 index 000000000000..9b1291da4965 --- /dev/null +++ b/toolchain/check/testdata/operators/builtin/fail_and_or_partial_constant.carbon @@ -0,0 +1,185 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_non_constant_result.carbon + +package FailNonConstantResult api; + +fn PartialConstant(x: bool) { + // CHECK:STDERR: fail_non_constant_result.carbon:[[@LINE+4]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var a: if true and x then bool else (); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var a: if true and x then bool else (); + // CHECK:STDERR: fail_non_constant_result.carbon:[[@LINE+4]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var b: if false or x then bool else (); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var b: if false or x then bool else (); +} + +// --- fail_despite_known_result.carbon + +package FailDespiteKnownResult api; + +fn KnownValueButNonConstantCondition(x: bool) { + // We choose not to give these cases constant values, even though we could, + // because they notionally involve evaluating a non-constant condition. + // CHECK:STDERR: fail_despite_known_result.carbon:[[@LINE+4]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var c: if x and false then bool else (); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var c: if x and false then bool else (); + // CHECK:STDERR: fail_despite_known_result.carbon:[[@LINE+3]]:10: ERROR: Cannot evaluate type expression. + // CHECK:STDERR: var d: if x or true then bool else (); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + var d: if x or true then bool else (); +} + +// CHECK:STDOUT: --- fail_non_constant_result.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal true [template] +// CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .PartialConstant = %PartialConstant +// CHECK:STDOUT: } +// CHECK:STDOUT: %PartialConstant: = fn_decl @PartialConstant [template] { +// CHECK:STDOUT: %x.loc4_20.1: bool = param x +// CHECK:STDOUT: @PartialConstant.%x: bool = bind_name x, %x.loc4_20.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @PartialConstant(%x: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc9_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc9_18.1: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: if %.loc9_13 br !and.rhs else br !and.result(%.loc9_18.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs: +// CHECK:STDOUT: %x.ref.loc9: bool = name_ref x, %x +// CHECK:STDOUT: br !and.result(%x.ref.loc9) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result: +// CHECK:STDOUT: %.loc9_18.2: bool = block_arg !and.result +// CHECK:STDOUT: if %.loc9_18.2 br !if.expr.then.loc9 else br !if.expr.else.loc9 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc9: +// CHECK:STDOUT: br !if.expr.result.loc9(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc9: +// CHECK:STDOUT: %.loc9_40.1: () = tuple_literal () +// CHECK:STDOUT: %.loc9_40.2: type = converted %.loc9_40.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc9(%.loc9_40.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc9: +// CHECK:STDOUT: %.loc9_10: type = block_arg !if.expr.result.loc9 +// CHECK:STDOUT: %a.var: ref = var a +// CHECK:STDOUT: %a: ref = bind_name a, %a.var +// CHECK:STDOUT: %.loc14_13: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc14_19.1: bool = not %.loc14_13 [template = constants.%.1] +// CHECK:STDOUT: %.loc14_19.2: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc14_19.1 br !or.rhs else br !or.result(%.loc14_19.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs: +// CHECK:STDOUT: %x.ref.loc14: bool = name_ref x, %x +// CHECK:STDOUT: br !or.result(%x.ref.loc14) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result: +// CHECK:STDOUT: %.loc14_19.3: bool = block_arg !or.result +// CHECK:STDOUT: if %.loc14_19.3 br !if.expr.then.loc14 else br !if.expr.else.loc14 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc14: +// CHECK:STDOUT: br !if.expr.result.loc14(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc14: +// CHECK:STDOUT: %.loc14_40.1: () = tuple_literal () +// CHECK:STDOUT: %.loc14_40.2: type = converted %.loc14_40.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc14(%.loc14_40.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc14: +// CHECK:STDOUT: %.loc14_10: type = block_arg !if.expr.result.loc14 +// CHECK:STDOUT: %b.var: ref = var b +// CHECK:STDOUT: %b: ref = bind_name b, %b.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_despite_known_result.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal false [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: bool = bool_literal true [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .KnownValueButNonConstantCondition = %KnownValueButNonConstantCondition +// CHECK:STDOUT: } +// CHECK:STDOUT: %KnownValueButNonConstantCondition: = fn_decl @KnownValueButNonConstantCondition [template] { +// CHECK:STDOUT: %x.loc4_38.1: bool = param x +// CHECK:STDOUT: @KnownValueButNonConstantCondition.%x: bool = bind_name x, %x.loc4_38.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @KnownValueButNonConstantCondition(%x: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref.loc11: bool = name_ref x, %x +// CHECK:STDOUT: %.loc11_15.1: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: if %x.ref.loc11 br !and.rhs else br !and.result(%.loc11_15.1) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.rhs: +// CHECK:STDOUT: %.loc11_19: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: br !and.result(%.loc11_19) +// CHECK:STDOUT: +// CHECK:STDOUT: !and.result: +// CHECK:STDOUT: %.loc11_15.2: bool = block_arg !and.result +// CHECK:STDOUT: if %.loc11_15.2 br !if.expr.then.loc11 else br !if.expr.else.loc11 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc11: +// CHECK:STDOUT: br !if.expr.result.loc11(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc11: +// CHECK:STDOUT: %.loc11_41.1: () = tuple_literal () +// CHECK:STDOUT: %.loc11_41.2: type = converted %.loc11_41.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: br !if.expr.result.loc11(%.loc11_41.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc11: +// CHECK:STDOUT: %.loc11_10: type = block_arg !if.expr.result.loc11 +// CHECK:STDOUT: %c.var: ref = var c +// CHECK:STDOUT: %c: ref = bind_name c, %c.var +// CHECK:STDOUT: %x.ref.loc15: bool = name_ref x, %x +// CHECK:STDOUT: %.loc15_15.1: bool = not %x.ref.loc15 +// CHECK:STDOUT: %.loc15_15.2: bool = bool_literal true [template = constants.%.3] +// CHECK:STDOUT: if %.loc15_15.1 br !or.rhs else br !or.result(%.loc15_15.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs: +// CHECK:STDOUT: %.loc15_18: bool = bool_literal true [template = constants.%.3] +// CHECK:STDOUT: br !or.result(%.loc15_18) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result: +// CHECK:STDOUT: %.loc15_15.3: bool = block_arg !or.result +// CHECK:STDOUT: if %.loc15_15.3 br !if.expr.then.loc15 else br !if.expr.else.loc15 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc15: +// CHECK:STDOUT: br !if.expr.result.loc15(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc15: +// CHECK:STDOUT: %.loc15_39.1: () = tuple_literal () +// CHECK:STDOUT: %.loc15_39.2: type = converted %.loc15_39.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: br !if.expr.result.loc15(%.loc15_39.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc15: +// CHECK:STDOUT: %.loc15_10: type = block_arg !if.expr.result.loc15 +// CHECK:STDOUT: %d.var: ref = var d +// CHECK:STDOUT: %d: ref = bind_name d, %d.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon b/toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon index 45ea93d5c982..433338d32b4a 100644 --- a/toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon +++ b/toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon @@ -160,7 +160,7 @@ fn Main() { // CHECK:STDOUT: br !if.expr.result.loc45(%.loc45_24) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc45: -// CHECK:STDOUT: %.loc45_4: i32 = block_arg !if.expr.result.loc45 +// CHECK:STDOUT: %.loc45_4: i32 = block_arg !if.expr.result.loc45 [template = constants.%.1] // CHECK:STDOUT: %.loc45_29: i32 = int_literal 3 [template = constants.%.4] // CHECK:STDOUT: assign %.loc45_4, %.loc45_29 // CHECK:STDOUT: %a.var: ref i32 = var a diff --git a/toolchain/check/testdata/operators/builtin/or.carbon b/toolchain/check/testdata/operators/builtin/or.carbon index 4f50284dd91f..e5c0c7bcc436 100644 --- a/toolchain/check/testdata/operators/builtin/or.carbon +++ b/toolchain/check/testdata/operators/builtin/or.carbon @@ -11,10 +11,24 @@ fn Or() -> bool { return F() or G(); } +fn Constant() { + var a: if true or true then bool else () = true; + var b: if true or false then bool else () = true; + var c: if false or true then bool else () = true; + var d: if false or false then bool else () = (); +} + +fn PartialConstant(x: bool) { + var a: if true or x then bool else () = true; +} + // CHECK:STDOUT: --- or.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: bool = bool_literal true [template] +// CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %.4: () = tuple_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -22,6 +36,8 @@ fn Or() -> bool { // CHECK:STDOUT: .F = %F // CHECK:STDOUT: .G = %G // CHECK:STDOUT: .Or = %Or +// CHECK:STDOUT: .Constant = %Constant +// CHECK:STDOUT: .PartialConstant = %PartialConstant // CHECK:STDOUT: } // CHECK:STDOUT: %F: = fn_decl @F [template] { // CHECK:STDOUT: %return.var.loc7: ref bool = var @@ -32,6 +48,11 @@ fn Or() -> bool { // CHECK:STDOUT: %Or: = fn_decl @Or [template] { // CHECK:STDOUT: %return.var.loc10: ref bool = var // CHECK:STDOUT: } +// CHECK:STDOUT: %Constant: = fn_decl @Constant [template] {} +// CHECK:STDOUT: %PartialConstant: = fn_decl @PartialConstant [template] { +// CHECK:STDOUT: %x.loc21_20.1: bool = param x +// CHECK:STDOUT: @PartialConstant.%x: bool = bind_name x, %x.loc21_20.1 +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> bool { @@ -68,3 +89,150 @@ fn Or() -> bool { // CHECK:STDOUT: return %.loc11_14.5 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @Constant() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc15_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc15_18.1: bool = not %.loc15_13 [template = constants.%.2] +// CHECK:STDOUT: %.loc15_18.2: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc15_18.1 br !or.rhs.loc15 else br !or.result.loc15(%.loc15_18.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs.loc15: +// CHECK:STDOUT: %.loc15_21: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: br !or.result.loc15(%.loc15_21) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result.loc15: +// CHECK:STDOUT: %.loc15_18.3: bool = block_arg !or.result.loc15 [template = constants.%.1] +// CHECK:STDOUT: if %.loc15_18.3 br !if.expr.then.loc15 else br !if.expr.else.loc15 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc15: +// CHECK:STDOUT: br !if.expr.result.loc15(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc15: +// CHECK:STDOUT: %.loc15_42.1: () = tuple_literal () +// CHECK:STDOUT: %.loc15_42.2: type = converted %.loc15_42.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc15(%.loc15_42.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc15: +// CHECK:STDOUT: %.loc15_10: type = block_arg !if.expr.result.loc15 [template = bool] +// CHECK:STDOUT: %a.var: ref bool = var a +// CHECK:STDOUT: %a: ref bool = bind_name a, %a.var +// CHECK:STDOUT: %.loc15_46: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: assign %a.var, %.loc15_46 +// CHECK:STDOUT: %.loc16_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc16_18.1: bool = not %.loc16_13 [template = constants.%.2] +// CHECK:STDOUT: %.loc16_18.2: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc16_18.1 br !or.rhs.loc16 else br !or.result.loc16(%.loc16_18.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs.loc16: +// CHECK:STDOUT: %.loc16_21: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: br !or.result.loc16(%.loc16_21) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result.loc16: +// CHECK:STDOUT: %.loc16_18.3: bool = block_arg !or.result.loc16 [template = constants.%.1] +// CHECK:STDOUT: if %.loc16_18.3 br !if.expr.then.loc16 else br !if.expr.else.loc16 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc16: +// CHECK:STDOUT: br !if.expr.result.loc16(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc16: +// CHECK:STDOUT: %.loc16_43.1: () = tuple_literal () +// CHECK:STDOUT: %.loc16_43.2: type = converted %.loc16_43.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc16(%.loc16_43.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc16: +// CHECK:STDOUT: %.loc16_10: type = block_arg !if.expr.result.loc16 [template = bool] +// CHECK:STDOUT: %b.var: ref bool = var b +// CHECK:STDOUT: %b: ref bool = bind_name b, %b.var +// CHECK:STDOUT: %.loc16_47: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: assign %b.var, %.loc16_47 +// CHECK:STDOUT: %.loc17_13: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc17_19.1: bool = not %.loc17_13 [template = constants.%.1] +// CHECK:STDOUT: %.loc17_19.2: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc17_19.1 br !or.rhs.loc17 else br !or.result.loc17(%.loc17_19.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs.loc17: +// CHECK:STDOUT: %.loc17_22: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: br !or.result.loc17(%.loc17_22) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result.loc17: +// CHECK:STDOUT: %.loc17_19.3: bool = block_arg !or.result.loc17 [template = constants.%.1] +// CHECK:STDOUT: if %.loc17_19.3 br !if.expr.then.loc17 else br !if.expr.else.loc17 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc17: +// CHECK:STDOUT: br !if.expr.result.loc17(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc17: +// CHECK:STDOUT: %.loc17_43.1: () = tuple_literal () +// CHECK:STDOUT: %.loc17_43.2: type = converted %.loc17_43.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc17(%.loc17_43.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc17: +// CHECK:STDOUT: %.loc17_10: type = block_arg !if.expr.result.loc17 [template = bool] +// CHECK:STDOUT: %c.var: ref bool = var c +// CHECK:STDOUT: %c: ref bool = bind_name c, %c.var +// CHECK:STDOUT: %.loc17_47: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: assign %c.var, %.loc17_47 +// CHECK:STDOUT: %.loc18_13: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc18_19.1: bool = not %.loc18_13 [template = constants.%.1] +// CHECK:STDOUT: %.loc18_19.2: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc18_19.1 br !or.rhs.loc18 else br !or.result.loc18(%.loc18_19.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs.loc18: +// CHECK:STDOUT: %.loc18_22: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: br !or.result.loc18(%.loc18_22) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result.loc18: +// CHECK:STDOUT: %.loc18_19.3: bool = block_arg !or.result.loc18 [template = constants.%.2] +// CHECK:STDOUT: if %.loc18_19.3 br !if.expr.then.loc18 else br !if.expr.else.loc18 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc18: +// CHECK:STDOUT: br !if.expr.result.loc18(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc18: +// CHECK:STDOUT: %.loc18_44.1: () = tuple_literal () +// CHECK:STDOUT: %.loc18_44.2: type = converted %.loc18_44.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc18(%.loc18_44.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc18: +// CHECK:STDOUT: %.loc18_10: type = block_arg !if.expr.result.loc18 [template = constants.%.3] +// CHECK:STDOUT: %d.var: ref () = var d +// CHECK:STDOUT: %d: ref () = bind_name d, %d.var +// CHECK:STDOUT: %.loc18_49.1: () = tuple_literal () +// CHECK:STDOUT: %.loc18_49.2: init () = tuple_init () to %d.var [template = constants.%.4] +// CHECK:STDOUT: %.loc18_49.3: init () = converted %.loc18_49.1, %.loc18_49.2 [template = constants.%.4] +// CHECK:STDOUT: assign %d.var, %.loc18_49.3 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @PartialConstant(%x: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc22_13: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc22_18.1: bool = not %.loc22_13 [template = constants.%.2] +// CHECK:STDOUT: %.loc22_18.2: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: if %.loc22_18.1 br !or.rhs else br !or.result(%.loc22_18.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.rhs: +// CHECK:STDOUT: %x.ref: bool = name_ref x, %x +// CHECK:STDOUT: br !or.result(%x.ref) +// CHECK:STDOUT: +// CHECK:STDOUT: !or.result: +// CHECK:STDOUT: %.loc22_18.3: bool = block_arg !or.result [template = constants.%.1] +// CHECK:STDOUT: if %.loc22_18.3 br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then: +// CHECK:STDOUT: br !if.expr.result(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else: +// CHECK:STDOUT: %.loc22_39.1: () = tuple_literal () +// CHECK:STDOUT: %.loc22_39.2: type = converted %.loc22_39.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result(%.loc22_39.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result: +// CHECK:STDOUT: %.loc22_10: type = block_arg !if.expr.result [template = bool] +// CHECK:STDOUT: %a.var: ref bool = var a +// CHECK:STDOUT: %a: ref bool = bind_name a, %a.var +// CHECK:STDOUT: %.loc22_43: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: assign %a.var, %.loc22_43 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/builtin/unary_op.carbon b/toolchain/check/testdata/operators/builtin/unary_op.carbon index f921e6ebb038..76cf800a04c5 100644 --- a/toolchain/check/testdata/operators/builtin/unary_op.carbon +++ b/toolchain/check/testdata/operators/builtin/unary_op.carbon @@ -11,16 +11,24 @@ fn Not(b: bool) -> bool { let not_true: bool = not true; let not_false: bool = not false; +fn Constant() { + var a: if not true then bool else () = (); + var b: if not false then bool else () = true; +} + // CHECK:STDOUT: --- unary_op.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: bool = bool_literal true [template] // CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %.4: () = tuple_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Not = %Not +// CHECK:STDOUT: .Constant = %Constant // CHECK:STDOUT: } // CHECK:STDOUT: %Not: = fn_decl @Not [template] { // CHECK:STDOUT: %b.loc7_8.1: bool = param b @@ -33,6 +41,7 @@ let not_false: bool = not false; // CHECK:STDOUT: %.loc12_27: bool = bool_literal false [template = constants.%.2] // CHECK:STDOUT: %.loc12_23: bool = not %.loc12_27 [template = constants.%.1] // CHECK:STDOUT: %not_false: bool = bind_name not_false, %.loc12_23 +// CHECK:STDOUT: %Constant: = fn_decl @Constant [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Not(%b: bool) -> bool { @@ -42,3 +51,46 @@ let not_false: bool = not false; // CHECK:STDOUT: return %.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @Constant() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc15_17: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: %.loc15_13: bool = not %.loc15_17 [template = constants.%.2] +// CHECK:STDOUT: if %.loc15_13 br !if.expr.then.loc15 else br !if.expr.else.loc15 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc15: +// CHECK:STDOUT: br !if.expr.result.loc15(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc15: +// CHECK:STDOUT: %.loc15_38.1: () = tuple_literal () +// CHECK:STDOUT: %.loc15_38.2: type = converted %.loc15_38.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc15(%.loc15_38.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc15: +// CHECK:STDOUT: %.loc15_10: type = block_arg !if.expr.result.loc15 [template = constants.%.3] +// CHECK:STDOUT: %a.var: ref () = var a +// CHECK:STDOUT: %a: ref () = bind_name a, %a.var +// CHECK:STDOUT: %.loc15_43.1: () = tuple_literal () +// CHECK:STDOUT: %.loc15_43.2: init () = tuple_init () to %a.var [template = constants.%.4] +// CHECK:STDOUT: %.loc15_43.3: init () = converted %.loc15_43.1, %.loc15_43.2 [template = constants.%.4] +// CHECK:STDOUT: assign %a.var, %.loc15_43.3 +// CHECK:STDOUT: %.loc16_17: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %.loc16_13: bool = not %.loc16_17 [template = constants.%.1] +// CHECK:STDOUT: if %.loc16_13 br !if.expr.then.loc16 else br !if.expr.else.loc16 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then.loc16: +// CHECK:STDOUT: br !if.expr.result.loc16(bool) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else.loc16: +// CHECK:STDOUT: %.loc16_39.1: () = tuple_literal () +// CHECK:STDOUT: %.loc16_39.2: type = converted %.loc16_39.1, constants.%.3 [template = constants.%.3] +// CHECK:STDOUT: br !if.expr.result.loc16(%.loc16_39.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result.loc16: +// CHECK:STDOUT: %.loc16_10: type = block_arg !if.expr.result.loc16 [template = bool] +// CHECK:STDOUT: %b.var: ref bool = var b +// CHECK:STDOUT: %b: ref bool = bind_name b, %b.var +// CHECK:STDOUT: %.loc16_43: bool = bool_literal true [template = constants.%.1] +// CHECK:STDOUT: assign %b.var, %.loc16_43 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/pointer/fail_address_of_value.carbon b/toolchain/check/testdata/pointer/fail_address_of_value.carbon index 9c3c31293584..072e5e77e167 100644 --- a/toolchain/check/testdata/pointer/fail_address_of_value.carbon +++ b/toolchain/check/testdata/pointer/fail_address_of_value.carbon @@ -184,7 +184,7 @@ fn AddressOfParam(param: i32) { // CHECK:STDOUT: br !and.result(%.loc49_14) // CHECK:STDOUT: // CHECK:STDOUT: !and.result: -// CHECK:STDOUT: %.loc49_10.2: bool = block_arg !and.result +// CHECK:STDOUT: %.loc49_10.2: bool = block_arg !and.result [template = constants.%.16] // CHECK:STDOUT: %.loc49_3: bool* = addr_of [template = ] // CHECK:STDOUT: %H.ref: = name_ref H, file.%H [template = file.%H] // CHECK:STDOUT: %.loc54_5.1: init {.a: i32} = call %H.ref()