From de2316bd0c38d1c5961bb038f76715f190495abf Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 22 Jan 2024 11:32:59 -0800 Subject: [PATCH] Fix crash when `and` or `or` appear outside a function. (#3633) Found by fuzzer. --- toolchain/check/handle_operator.cpp | 4 +- .../fail_and_or_not_in_function.carbon | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 toolchain/check/testdata/operators/fail_and_or_not_in_function.carbon diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index ec1f200c4cbe..fcb930470dc3 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -344,7 +344,7 @@ static auto HandleShortCircuitOperand(Context& context, context.inst_block_stack().Pop(); context.inst_block_stack().Push(end_block_id); context.inst_block_stack().Push(rhs_block_id); - context.AddCurrentCodeBlockToFunction(); + context.AddCurrentCodeBlockToFunction(parse_node); // HandleShortCircuitOperator will follow, and doesn't need the operand on the // node stack. @@ -379,7 +379,7 @@ static auto HandleShortCircuitOperator(Context& context, auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1); context.AddInst({parse_node, SemIR::BranchWithArg{resume_block_id, rhs_id}}); context.inst_block_stack().Pop(); - context.AddCurrentCodeBlockToFunction(); + context.AddCurrentCodeBlockToFunction(parse_node); // Collect the result from either the first or second operand. context.AddInstAndPush( diff --git a/toolchain/check/testdata/operators/fail_and_or_not_in_function.carbon b/toolchain/check/testdata/operators/fail_and_or_not_in_function.carbon new file mode 100644 index 000000000000..f0b05f8f2e62 --- /dev/null +++ b/toolchain/check/testdata/operators/fail_and_or_not_in_function.carbon @@ -0,0 +1,66 @@ +// 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 + +// TODO: Make this a compile-time function. +fn F(b: bool) -> type { + return if b then i32 else f64; +} + +// TODO: Short-circuit operators should be permitted outside functions. +// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+9]]:11: ERROR: Cannot evaluate type expression. +// CHECK:STDERR: var and_: F(true and true); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+6]]:13: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. +// CHECK:STDERR: var and_: F(true and true); +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+3]]:13: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. +// CHECK:STDERR: var and_: F(true and true); +// CHECK:STDERR: ^~~~~~~~~~~~~ +var and_: F(true and true); + +// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+9]]:10: ERROR: Cannot evaluate type expression. +// CHECK:STDERR: var or_: F(true or true); +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+6]]:12: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. +// CHECK:STDERR: var or_: F(true or true); +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+3]]:12: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. +// CHECK:STDERR: var or_: F(true or true); +// CHECK:STDERR: ^~~~~~~~~~~~ +var or_: F(true or true); + +// CHECK:STDOUT: --- fail_and_or_not_in_function.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal true [template] +// CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %.loc33_17: bool = block_arg +// CHECK:STDOUT: %.loc33_11.1: init type = call (%.loc33_17) +// CHECK:STDOUT: %.loc33_24: type = value_of_initializer %.loc33_11.1 +// CHECK:STDOUT: %.loc33_11.2: type = converted %.loc33_11.1, %.loc33_24 +// CHECK:STDOUT: %or_.var: ref = var or_ +// CHECK:STDOUT: %or_: ref = bind_name or_, %or_.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%b: bool) -> type { +// 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(f64) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result: +// CHECK:STDOUT: %.loc9: type = block_arg !if.expr.result +// CHECK:STDOUT: return %.loc9 +// CHECK:STDOUT: } +// CHECK:STDOUT: