Files
carbon-lang/toolchain/check/testdata/interop/cpp/function/import/constexpr.carbon
T
Richard Smith 90f6654b48 Fix crash attempting constant evaluation of a constexpr constructor. (#7514)
We can't use a `CallExpr` to call a constructor; use a
`CXXConstructExpr` instead. While this fixes the crash and gets us past
the initial constant evaluation, we still can't map the constant value
back into Carbon, so this doesn't actually make constexpr constructors
work yet. But it does stop Clang from crashing.

Fixes #7498.
2026-07-16 15:17:08 +00:00

275 lines
15 KiB
Plaintext

// 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
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon
// EXTRA-ARGS: --clang-arg=--std=c++20
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/import/constexpr.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/import/constexpr.carbon
// --- basic.carbon
library "[[@TEST_NAME]]";
import Cpp inline '''
constexpr int f(int a, int b) { return a + b; }
''';
let a: array(i32, Cpp.f(1, 2)) = (1, 2, 3);
// --- bool_param.carbon
library "[[@TEST_NAME]]";
import Cpp inline '''
constexpr int f(bool b) {
return b ? 3 : 0;
}
''';
let a: array(i32, Cpp.f(true)) = (1, 2, 3);
// --- float_param.carbon
library "[[@TEST_NAME]]";
import Cpp inline '''
constexpr int f(float b) {
return static_cast<int>(b);
}
''';
let a: array(i32, Cpp.f(3.0)) = (1, 2, 3);
// --- return_bool.carbon
library "[[@TEST_NAME]]";
import Cpp inline '''
constexpr bool f() {
return true;
}
''';
musteval fn F(b: bool) -> i32 {
return if b then 3 else 0;
}
let a: array(i32, F(Cpp.f())) = (1, 2, 3);
// --- fail_invalid_constant_eval.carbon
library "[[@TEST_NAME]]";
import Cpp inline '''
constexpr int arr[1] = {0};
constexpr int f(int a) {
return arr[a];
}
''';
// CHECK:STDERR: fail_invalid_constant_eval.carbon:[[@LINE+4]]:19: error: array bound is not a constant [InvalidArrayExpr]
// CHECK:STDERR: let a: array(i32, Cpp.f(5)) = (1, 2, 3);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
let a: array(i32, Cpp.f(5)) = (1, 2, 3);
// --- nonconstant_constexpr_call.carbon
library "[[@TEST_NAME]]";
import Cpp;
var a: i32 = 0;
inline Cpp '''
constexpr int f(const int &r) { return r; }
''';
// OK, runtime call.
var b: i32 = Cpp.f(a);
// --- fail_nonconstant_consteval_call.carbon
library "[[@TEST_NAME]]";
import Cpp;
var a: i32 = 0;
// TODO: This is failing when building the thunk, not when calling it.
inline Cpp '''
// CHECK:STDERR: fail_nonconstant_consteval_call.carbon:[[@LINE+9]]:15: error: call to consteval function 'f' is not a constant expression [CppInteropParseError]
// CHECK:STDERR: 19 | consteval int f(const int &r) { return r; }
// CHECK:STDERR: | ^
// CHECK:STDERR: fail_nonconstant_consteval_call.carbon:[[@LINE+6]]:15: note: function parameter 'r' with unknown value cannot be used in a constant expression [CppInteropParseNote]
// CHECK:STDERR: 19 | consteval int f(const int &r) { return r; }
// CHECK:STDERR: | ^
// CHECK:STDERR: fail_nonconstant_consteval_call.carbon:[[@LINE+3]]:15: note: declared here [CppInteropParseNote]
// CHECK:STDERR: 19 | consteval int f(const int &r) { return r; }
// CHECK:STDERR: | ^
consteval int f(const int &r) { return r; }
''';
// CHECK:STDERR: fail_nonconstant_consteval_call.carbon:[[@LINE+11]]:14: note: in thunk for C++ function used here [InCppThunk]
// CHECK:STDERR: var b: i32 = Cpp.f(a);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_nonconstant_consteval_call.carbon:[[@LINE+7]]:14: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction]
// CHECK:STDERR: var b: i32 = Cpp.f(a);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_nonconstant_consteval_call.carbon:[[@LINE-10]]:15: note: compile-time-only function declared here [CompTimeOnlyFunctionHere]
// CHECK:STDERR: consteval int f(const int &r) { return r; }
// CHECK:STDERR: ^
// CHECK:STDERR:
var b: i32 = Cpp.f(a);
// --- fail_todo_constructor.carbon
library "[[@TEST_NAME]]";
import Cpp;
inline Cpp '''
struct C {
constexpr C(int a) : a(a) {}
int a;
};
''';
// TODO: This should probably be allowed.
eval fn F() -> i32 {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_constructor.carbon:[[@LINE+3]]:18: error: expression is runtime; expected constant [EvalRequiresConstantValue]
// CHECK:STDERR: let c: Cpp.C = 3;
// CHECK:STDERR: ^
let c: Cpp.C = 3;
return c.a;
//@dump-sem-ir-end
}
// CHECK:STDERR: fail_todo_constructor.carbon:[[@LINE+4]]:19: note: in call to F here [InCallToEvalFn]
// CHECK:STDERR: let a: array(i32, F()) = (1, 2, 3);
// CHECK:STDERR: ^~~
// CHECK:STDERR:
let a: array(i32, F()) = (1, 2, 3);
// CHECK:STDOUT: --- fail_todo_constructor.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete]
// CHECK:STDOUT: %pattern_type.bbc: type = pattern_type %C [concrete]
// CHECK:STDOUT: %c.patt: %pattern_type.bbc = value_binding_pattern c [concrete]
// CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
// CHECK:STDOUT: %ptr.0a2: type = ptr_type %C [concrete]
// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.type.914: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl.0e9(%To) [symbolic]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.845: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74 = struct_value () [symbolic]
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
// CHECK:STDOUT: %ImplicitAs.impl_witness.a23: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.b3c, @Core.IntLiteral.as.ImplicitAs.impl.0e9(%int_32) [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.2ba: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl.0e9(%int_32) [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.2ba = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.facet.9f1: %ImplicitAs.type.914 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.a23) [concrete]
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.740: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.9f1) [concrete]
// CHECK:STDOUT: %.1c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.740, %ImplicitAs.facet.9f1 [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39 [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %int_3.410: %i32 = int_value 3 [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.ac8: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.5e0: %Int.as.Copy.impl.Op.type.ac8 = struct_value () [symbolic]
// CHECK:STDOUT: %Copy.impl_witness.0e0: <witness> = impl_witness imports.%Copy.impl_witness_table.367, @Int.as.Copy.impl(%int_32) [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.3f6: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.4f6: %Int.as.Copy.impl.Op.type.3f6 = struct_value () [concrete]
// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.0e0) [concrete]
// CHECK:STDOUT: %Copy.WithSelf.Op.type.d09: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
// CHECK:STDOUT: %.7a6: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d09, %Copy.facet [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.4f6, @Int.as.Copy.impl.Op(%int_32) [concrete]
// CHECK:STDOUT: %C.cpp_destructor.type: type = fn_type @C.cpp_destructor [concrete]
// CHECK:STDOUT: %C.cpp_destructor: %C.cpp_destructor.type = struct_value () [concrete]
// CHECK:STDOUT: %C.Op.type: type = fn_type @C.Op [concrete]
// CHECK:STDOUT: %C.Op: %C.Op.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import_ref.edf: @Core.IntLiteral.as.ImplicitAs.impl.0e9.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.0e9.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.845)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.b3c = impl_witness_table (%Core.import_ref.edf), @Core.IntLiteral.as.ImplicitAs.impl.0e9 [concrete]
// CHECK:STDOUT: %Core.import_ref.cd6: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.ac8) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.5e0)]
// CHECK:STDOUT: %Copy.impl_witness_table.367 = impl_witness_table (%Core.import_ref.cd6), @Int.as.Copy.impl [concrete]
// CHECK:STDOUT: %C.cpp_destructor.decl: %C.cpp_destructor.type = fn_decl @C.cpp_destructor [concrete = constants.%C.cpp_destructor] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> out %return.param: %i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
// CHECK:STDOUT: %.loc19_18.1: ref %C = temporary_storage
// CHECK:STDOUT: %impl.elem0.loc19: %.1c5 = impl_witness_access constants.%ImplicitAs.impl_witness.a23, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e39]
// CHECK:STDOUT: %bound_method.loc19_18.1: <bound method> = bound_method %int_3, %impl.elem0.loc19 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
// CHECK:STDOUT: %specific_fn.loc19: <specific function> = specific_function %impl.elem0.loc19, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
// CHECK:STDOUT: %bound_method.loc19_18.2: <bound method> = bound_method %int_3, %specific_fn.loc19 [concrete = constants.%bound_method]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc19_18.2(%int_3) [concrete = constants.%int_3.410]
// CHECK:STDOUT: %.loc19_18.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.410]
// CHECK:STDOUT: %.loc19_18.3: %i32 = converted %int_3, %.loc19_18.2 [concrete = constants.%int_3.410]
// CHECK:STDOUT: %addr: %ptr.0a2 = addr_of %.loc19_18.1
// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc19_18.3, %addr)
// CHECK:STDOUT: %.loc19_18.4: init %C to %.loc19_18.1 = mark_in_place_init %C__carbon_thunk.call
// CHECK:STDOUT: %.loc19_18.5: init %C = converted %int_3, %.loc19_18.4
// CHECK:STDOUT: %.loc19_18.6: ref %C = temporary %.loc19_18.1, %.loc19_18.5
// CHECK:STDOUT: %.loc19_18.7: %C = acquire_value %.loc19_18.6
// CHECK:STDOUT: %.loc19_13: type = splice_block %C.ref [concrete = constants.%C] {
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: }
// CHECK:STDOUT: %c: %C = wrapper_binding c, %.loc19_18.7
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %c.patt: %pattern_type.bbc = value_binding_pattern c [concrete = constants.%c.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %c.ref: %C = name_ref c, %c
// CHECK:STDOUT: %a.ref: %C.elem = name_ref a, @C.%.1 [concrete = @C.%.1]
// CHECK:STDOUT: %.loc20_11.1: ref %i32 = class_element_access %c.ref, element0
// CHECK:STDOUT: %.loc20_11.2: %i32 = acquire_value %.loc20_11.1
// CHECK:STDOUT: %impl.elem0.loc20: %.7a6 = impl_witness_access constants.%Copy.impl_witness.0e0, element0 [concrete = constants.%Int.as.Copy.impl.Op.4f6]
// CHECK:STDOUT: %bound_method.loc20_11.1: <bound method> = bound_method %.loc20_11.2, %impl.elem0.loc20
// CHECK:STDOUT: %specific_fn.loc20: <specific function> = specific_function %impl.elem0.loc20, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
// CHECK:STDOUT: %bound_method.loc20_11.2: <bound method> = bound_method %.loc20_11.2, %specific_fn.loc20
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc20_11.2(%.loc20_11.2)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %C.Op.bound: <bound method> = bound_method %.loc19_18.6, constants.%C.Op
// CHECK:STDOUT: %Op.ref: %C.cpp_destructor.type = name_ref Op, imports.%C.cpp_destructor.decl [concrete = constants.%C.cpp_destructor]
// CHECK:STDOUT: %C.cpp_destructor.bound: <bound method> = bound_method %.loc19_18.6, %Op.ref
// CHECK:STDOUT: %C.cpp_destructor.call: init %empty_tuple.type = call %C.cpp_destructor.bound(%.loc19_18.6)
// CHECK:STDOUT: return %Int.as.Copy.impl.Op.call
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: