diff --git a/toolchain/check/cpp/type_mapping.cpp b/toolchain/check/cpp/type_mapping.cpp index 914921e03573..6953f132e98b 100644 --- a/toolchain/check/cpp/type_mapping.cpp +++ b/toolchain/check/cpp/type_mapping.cpp @@ -514,6 +514,9 @@ static auto InventPrimitiveClangArg(Context& context, FormInfo form) break; case SemIR::ExprCategory::Value: + value_kind = clang::ExprValueKind::VK_PRValue; + break; + case SemIR::ExprCategory::ReprInitializing: case SemIR::ExprCategory::InPlaceInitializing: value_kind = clang::ExprValueKind::VK_PRValue; @@ -552,6 +555,15 @@ static auto InventPrimitiveClangArg(Context& context, FormInfo form) return nullptr; } + // Map a value expression to a const-qualified prvalue so that overload + // resolution doesn't think it's a suitable argument for a non-const-qualified + // object parameter or a `T&&` parameter. We can only do this for class + // prvalues, because non-class non-array prvalues can't be qualified in C++. + if (form.category == SemIR::ExprCategory::Value && + arg_cpp_type->isRecordType()) { + arg_cpp_type = context.ast_context().getConstType(arg_cpp_type); + } + // TODO: Avoid heap allocating more of these on every call. Either cache them // somewhere or put them on the stack. return new (context.ast_context()) diff --git a/toolchain/check/testdata/interop/cpp/class/method.carbon b/toolchain/check/testdata/interop/cpp/class/method.carbon index b66bb47ebd6f..d9c58270fe2d 100644 --- a/toolchain/check/testdata/interop/cpp/class/method.carbon +++ b/toolchain/check/testdata/interop/cpp/class/method.carbon @@ -48,12 +48,12 @@ fn F(v: Cpp.HasQualifiers, p: Cpp.HasQualifiers*) { library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+8]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./object_param_qualifiers.h:10:8: error: 'this' argument to member function 'ref_ref_this' is an lvalue, but function has rvalue ref-qualifier [CppInteropParseError] -// CHECK:STDERR: 10 | void ref_ref_this() &&; +// CHECK:STDERR: ./object_param_qualifiers.h:11:8: error: 'this' argument to member function 'const_ref_ref_this' is an lvalue, but function has rvalue ref-qualifier [CppInteropParseError] +// CHECK:STDERR: 11 | void const_ref_ref_this() const&&; // CHECK:STDERR: | ^ // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./object_param_qualifiers.h:10:8: note: 'ref_ref_this' declared here [CppInteropParseNote] -// CHECK:STDERR: 10 | void ref_ref_this() &&; +// CHECK:STDERR: ./object_param_qualifiers.h:11:8: note: 'const_ref_ref_this' declared here [CppInteropParseNote] +// CHECK:STDERR: 11 | void const_ref_ref_this() const&&; // CHECK:STDERR: | ^ import Cpp library "object_param_qualifiers.h"; @@ -65,43 +65,44 @@ fn Value(v: Cpp.HasQualifiers) { v.ref_this(); - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+12]]:3: note: in thunk for C++ function used here [InCppThunk] - // CHECK:STDERR: v.ref_ref_this(); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-14]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./object_param_qualifiers.h:11:8: error: 'this' argument to member function 'const_ref_ref_this' is an lvalue, but function has rvalue ref-qualifier [CppInteropParseError] - // CHECK:STDERR: 11 | void const_ref_ref_this() const&&; - // CHECK:STDERR: | ^ - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-18]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./object_param_qualifiers.h:11:8: note: 'const_ref_ref_this' declared here [CppInteropParseNote] - // CHECK:STDERR: 11 | void const_ref_ref_this() const&&; - // CHECK:STDERR: | ^ v.ref_ref_this(); - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+23]]:3: note: in thunk for C++ function used here [InCppThunk] + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+36]]:3: note: in thunk for C++ function used here [InCppThunk] // CHECK:STDERR: v.const_ref_ref_this(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-25]]:3: error: value expression passed to reference parameter [ValueForRefParam] - // CHECK:STDERR: v.plain(); - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-28]]:3: note: initializing function parameter [InCallToFunctionParam] - // CHECK:STDERR: v.plain(); - // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-13]]:11: error: no matching function for call to 'plain' [CppInteropParseError] + // CHECK:STDERR: 15 | v.plain(); + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-19]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./object_param_qualifiers.h:3:8: note: candidate function not viable: 'this' argument has type 'const HasQualifiers', but method is not marked const [CppInteropParseNote] + // CHECK:STDERR: 3 | void plain(); + // CHECK:STDERR: | ^ // CHECK:STDERR: - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-29]]:3: error: semantics TODO: `Unsupported: object parameter type: volatile struct HasQualifiers &` [SemanticsTodo] - // CHECK:STDERR: v.volatile_this(); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-18]]:19: error: no matching function for call to 'volatile_this' [CppInteropParseError] + // CHECK:STDERR: 18 | v.volatile_this(); + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-27]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./object_param_qualifiers.h:5:8: note: candidate function not viable: 'this' argument has type 'const HasQualifiers', but method is not marked const [CppInteropParseNote] + // CHECK:STDERR: 5 | void volatile_this() volatile; + // CHECK:STDERR: | ^ // CHECK:STDERR: - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-31]]:14: error: no matching function for call to 'ref_this' [CppInteropParseError] + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-24]]:14: error: no matching function for call to 'ref_this' [CppInteropParseError] // CHECK:STDERR: 20 | v.ref_this(); // CHECK:STDERR: | ^ - // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-42]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./object_param_qualifiers.h:7:8: note: candidate function not viable: expects an lvalue for object argument [CppInteropParseNote] + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-35]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./object_param_qualifiers.h:7:8: note: candidate function not viable: 'this' argument has type 'const HasQualifiers', but method is not marked const [CppInteropParseNote] // CHECK:STDERR: 7 | void ref_this() &; // CHECK:STDERR: | ^ // CHECK:STDERR: + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-30]]:18: error: no matching function for call to 'ref_ref_this' [CppInteropParseError] + // CHECK:STDERR: 22 | v.ref_ref_this(); + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-43]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./object_param_qualifiers.h:10:8: note: candidate function not viable: 'this' argument has type 'const HasQualifiers', but method is not marked const [CppInteropParseNote] + // CHECK:STDERR: 10 | void ref_ref_this() &&; + // CHECK:STDERR: | ^ + // CHECK:STDERR: v.const_ref_ref_this(); } @@ -150,7 +151,13 @@ fn Ref(p: Cpp.HasQualifiers*) { // --- object_param_qualifiers_overloaded.h -struct HasQualifiers { +struct NoRefQualifier { + int* _Nonnull F(); + int F() const; + void F() volatile; +}; + +struct WithRefQualifier { int* _Nonnull F() &; int F() const &; void F() volatile &; @@ -162,7 +169,14 @@ library "[[@TEST_NAME]]"; import Cpp library "object_param_qualifiers_overloaded.h"; -fn CallF(v: Cpp.HasQualifiers, p: Cpp.HasQualifiers*) { +fn CallFNoRefQualifier(v: Cpp.NoRefQualifier, p: Cpp.NoRefQualifier*) { + //@dump-sem-ir-begin + var unused a: i32 = v.F(); + var unused b: i32* = p->F(); + //@dump-sem-ir-end +} + +fn CallFWithRefQualifier(v: Cpp.WithRefQualifier, p: Cpp.WithRefQualifier*) { //@dump-sem-ir-begin var unused a: i32 = v.F(); var unused b: i32* = p->F(); @@ -332,56 +346,81 @@ fn Call(e: Cpp.ExplicitObjectParam, n: i32, a: Cpp.Another) { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %HasQualifiers: type = class_type @HasQualifiers [concrete] -// CHECK:STDOUT: %pattern_type.e15: type = pattern_type %HasQualifiers [concrete] -// CHECK:STDOUT: %ptr.ec3: type = ptr_type %HasQualifiers [concrete] +// CHECK:STDOUT: %NoRefQualifier: type = class_type @NoRefQualifier [concrete] +// CHECK:STDOUT: %pattern_type.a91: type = pattern_type %NoRefQualifier [concrete] +// CHECK:STDOUT: %ptr.2a2: type = ptr_type %NoRefQualifier [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] -// CHECK:STDOUT: %HasQualifiers.F.cpp_overload_set.type: type = cpp_overload_set_type @HasQualifiers.F.cpp_overload_set [concrete] -// CHECK:STDOUT: %HasQualifiers.F.cpp_overload_set.value: %HasQualifiers.F.cpp_overload_set.type = cpp_overload_set_value @HasQualifiers.F.cpp_overload_set [concrete] -// CHECK:STDOUT: %F__carbon_thunk.type: type = fn_type @F__carbon_thunk [concrete] -// CHECK:STDOUT: %F__carbon_thunk: %F__carbon_thunk.type = struct_value () [concrete] +// CHECK:STDOUT: %NoRefQualifier.F.cpp_overload_set.type: type = cpp_overload_set_type @NoRefQualifier.F.cpp_overload_set [concrete] +// CHECK:STDOUT: %NoRefQualifier.F.cpp_overload_set.value: %NoRefQualifier.F.cpp_overload_set.type = cpp_overload_set_value @NoRefQualifier.F.cpp_overload_set [concrete] +// CHECK:STDOUT: %F__carbon_thunk.type.eda1ac.1: type = fn_type @F__carbon_thunk.1 [concrete] +// CHECK:STDOUT: %F__carbon_thunk.0cd6a8.1: %F__carbon_thunk.type.eda1ac.1 = struct_value () [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] -// CHECK:STDOUT: %HasQualifiers.F.type.d208f0.2: type = fn_type @HasQualifiers.F.2 [concrete] -// CHECK:STDOUT: %HasQualifiers.F.efd4e4.2: %HasQualifiers.F.type.d208f0.2 = struct_value () [concrete] +// CHECK:STDOUT: %NoRefQualifier.F.type.b65611.2: type = fn_type @NoRefQualifier.F.2 [concrete] +// CHECK:STDOUT: %NoRefQualifier.F.d50a5a.2: %NoRefQualifier.F.type.b65611.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc9 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc8 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] +// CHECK:STDOUT: %WithRefQualifier: type = class_type @WithRefQualifier [concrete] +// CHECK:STDOUT: %pattern_type.4ed: type = pattern_type %WithRefQualifier [concrete] +// CHECK:STDOUT: %ptr.c86: type = ptr_type %WithRefQualifier [concrete] +// CHECK:STDOUT: %WithRefQualifier.F.cpp_overload_set.type: type = cpp_overload_set_type @WithRefQualifier.F.cpp_overload_set [concrete] +// CHECK:STDOUT: %WithRefQualifier.F.cpp_overload_set.value: %WithRefQualifier.F.cpp_overload_set.type = cpp_overload_set_value @WithRefQualifier.F.cpp_overload_set [concrete] +// CHECK:STDOUT: %F__carbon_thunk.type.eda1ac.2: type = fn_type @F__carbon_thunk.2 [concrete] +// CHECK:STDOUT: %F__carbon_thunk.0cd6a8.2: %F__carbon_thunk.type.eda1ac.2 = struct_value () [concrete] +// CHECK:STDOUT: %WithRefQualifier.F.type.7c19e2.2: type = fn_type @WithRefQualifier.F.2 [concrete] +// CHECK:STDOUT: %WithRefQualifier.F.d90b51.2: %WithRefQualifier.F.type.7c19e2.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { -// CHECK:STDOUT: %HasQualifiers.F.cpp_overload_set.value: %HasQualifiers.F.cpp_overload_set.type = cpp_overload_set_value @HasQualifiers.F.cpp_overload_set [concrete = constants.%HasQualifiers.F.cpp_overload_set.value] -// CHECK:STDOUT: %F__carbon_thunk.decl: %F__carbon_thunk.type = fn_decl @F__carbon_thunk [concrete = constants.%F__carbon_thunk] { +// CHECK:STDOUT: %NoRefQualifier.F.cpp_overload_set.value: %NoRefQualifier.F.cpp_overload_set.type = cpp_overload_set_value @NoRefQualifier.F.cpp_overload_set [concrete = constants.%NoRefQualifier.F.cpp_overload_set.value] +// CHECK:STDOUT: %F__carbon_thunk.decl.e1b8ec.1: %F__carbon_thunk.type.eda1ac.1 = fn_decl @F__carbon_thunk.1 [concrete = constants.%F__carbon_thunk.0cd6a8.1] { // CHECK:STDOUT: // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: } -// CHECK:STDOUT: %HasQualifiers.F.decl.f862ea.2: %HasQualifiers.F.type.d208f0.2 = fn_decl @HasQualifiers.F.2 [concrete = constants.%HasQualifiers.F.efd4e4.2] { -// CHECK:STDOUT: %self.patt: %pattern_type.e15 = ref_binding_pattern self [concrete] -// CHECK:STDOUT: %self.param_patt: %pattern_type.e15 = ref_param_pattern %self.patt [concrete] +// CHECK:STDOUT: %NoRefQualifier.F.decl.3dba03.2: %NoRefQualifier.F.type.b65611.2 = fn_decl @NoRefQualifier.F.2 [concrete = constants.%NoRefQualifier.F.d50a5a.2] { +// CHECK:STDOUT: %self.patt: %pattern_type.a91 = ref_binding_pattern self [concrete] +// CHECK:STDOUT: %self.param_patt: %pattern_type.a91 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: // CHECK:STDOUT: } { // CHECK:STDOUT: -// CHECK:STDOUT: %self.param: ref %HasQualifiers = ref_param call_param0 -// CHECK:STDOUT: %self: ref %HasQualifiers = ref_binding self, %self.param +// CHECK:STDOUT: %self.param: ref %NoRefQualifier = ref_param call_param0 +// CHECK:STDOUT: %self: ref %NoRefQualifier = ref_binding self, %self.param +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %WithRefQualifier.F.cpp_overload_set.value: %WithRefQualifier.F.cpp_overload_set.type = cpp_overload_set_value @WithRefQualifier.F.cpp_overload_set [concrete = constants.%WithRefQualifier.F.cpp_overload_set.value] +// CHECK:STDOUT: %F__carbon_thunk.decl.e1b8ec.2: %F__carbon_thunk.type.eda1ac.2 = fn_decl @F__carbon_thunk.2 [concrete = constants.%F__carbon_thunk.0cd6a8.2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %WithRefQualifier.F.decl.e3f32b.2: %WithRefQualifier.F.type.7c19e2.2 = fn_decl @WithRefQualifier.F.2 [concrete = constants.%WithRefQualifier.F.d90b51.2] { +// CHECK:STDOUT: %self.patt: %pattern_type.4ed = ref_binding_pattern self [concrete] +// CHECK:STDOUT: %self.param_patt: %pattern_type.4ed = ref_param_pattern %self.patt [concrete] +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %self.param: ref %WithRefQualifier = ref_param call_param0 +// CHECK:STDOUT: %self: ref %WithRefQualifier = ref_binding self, %self.param // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @CallF(%v.param: %HasQualifiers, %p.param: %ptr.ec3) { +// CHECK:STDOUT: fn @CallFNoRefQualifier(%v.param: %NoRefQualifier, %p.param: %ptr.2a2) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.7ce = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %i32 = var %a.var_patt -// CHECK:STDOUT: %v.ref: %HasQualifiers = name_ref v, %v -// CHECK:STDOUT: %F.ref.loc8: %HasQualifiers.F.cpp_overload_set.type = name_ref F, imports.%HasQualifiers.F.cpp_overload_set.value [concrete = constants.%HasQualifiers.F.cpp_overload_set.value] +// CHECK:STDOUT: %v.ref: %NoRefQualifier = name_ref v, %v +// CHECK:STDOUT: %F.ref.loc8: %NoRefQualifier.F.cpp_overload_set.type = name_ref F, imports.%NoRefQualifier.F.cpp_overload_set.value [concrete = constants.%NoRefQualifier.F.cpp_overload_set.value] // CHECK:STDOUT: %bound_method.loc8: = bound_method %v.ref, %F.ref.loc8 -// CHECK:STDOUT: %F__carbon_thunk.call: init %i32 = call imports.%F__carbon_thunk.decl(%v.ref) +// CHECK:STDOUT: %F__carbon_thunk.call: init %i32 = call imports.%F__carbon_thunk.decl.e1b8ec.1(%v.ref) // CHECK:STDOUT: assign %a.var, %F__carbon_thunk.call // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: ref %i32 = ref_binding a, %a.var @@ -390,12 +429,12 @@ fn Call(e: Cpp.ExplicitObjectParam, n: i32, a: Cpp.Another) { // CHECK:STDOUT: %b.var_patt: %pattern_type.fe8 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %ptr.235 = var %b.var_patt -// CHECK:STDOUT: %p.ref: %ptr.ec3 = name_ref p, %p -// CHECK:STDOUT: %.loc9_25: ref %HasQualifiers = deref %p.ref -// CHECK:STDOUT: %F.ref.loc9: %HasQualifiers.F.cpp_overload_set.type = name_ref F, imports.%HasQualifiers.F.cpp_overload_set.value [concrete = constants.%HasQualifiers.F.cpp_overload_set.value] +// CHECK:STDOUT: %p.ref: %ptr.2a2 = name_ref p, %p +// CHECK:STDOUT: %.loc9_25: ref %NoRefQualifier = deref %p.ref +// CHECK:STDOUT: %F.ref.loc9: %NoRefQualifier.F.cpp_overload_set.type = name_ref F, imports.%NoRefQualifier.F.cpp_overload_set.value [concrete = constants.%NoRefQualifier.F.cpp_overload_set.value] // CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_25, %F.ref.loc9 -// CHECK:STDOUT: %HasQualifiers.F.call: init %ptr.235 = call imports.%HasQualifiers.F.decl.f862ea.2(%.loc9_25) -// CHECK:STDOUT: assign %b.var, %HasQualifiers.F.call +// CHECK:STDOUT: %NoRefQualifier.F.call: init %ptr.235 = call imports.%NoRefQualifier.F.decl.3dba03.2(%.loc9_25) +// CHECK:STDOUT: assign %b.var, %NoRefQualifier.F.call // CHECK:STDOUT: %.loc9_20: type = splice_block %ptr.loc9 [concrete = constants.%ptr.235] { // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr.loc9: type = ptr_type %i32.loc9 [concrete = constants.%ptr.235] @@ -412,6 +451,43 @@ fn Call(e: Cpp.ExplicitObjectParam, n: i32, a: Cpp.Another) { // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc8(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: +// CHECK:STDOUT: fn @CallFWithRefQualifier(%v.param: %WithRefQualifier, %p.param: %ptr.c86) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.7ce = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.7ce = var_pattern %a.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a.var: ref %i32 = var %a.var_patt +// CHECK:STDOUT: %v.ref: %WithRefQualifier = name_ref v, %v +// CHECK:STDOUT: %F.ref.loc15: %WithRefQualifier.F.cpp_overload_set.type = name_ref F, imports.%WithRefQualifier.F.cpp_overload_set.value [concrete = constants.%WithRefQualifier.F.cpp_overload_set.value] +// CHECK:STDOUT: %bound_method.loc15: = bound_method %v.ref, %F.ref.loc15 +// CHECK:STDOUT: %F__carbon_thunk.call: init %i32 = call imports.%F__carbon_thunk.decl.e1b8ec.2(%v.ref) +// CHECK:STDOUT: assign %a.var, %F__carbon_thunk.call +// CHECK:STDOUT: %i32.loc15: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %a: ref %i32 = ref_binding a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.fe8 = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.fe8 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b.var: ref %ptr.235 = var %b.var_patt +// CHECK:STDOUT: %p.ref: %ptr.c86 = name_ref p, %p +// CHECK:STDOUT: %.loc16_25: ref %WithRefQualifier = deref %p.ref +// CHECK:STDOUT: %F.ref.loc16: %WithRefQualifier.F.cpp_overload_set.type = name_ref F, imports.%WithRefQualifier.F.cpp_overload_set.value [concrete = constants.%WithRefQualifier.F.cpp_overload_set.value] +// CHECK:STDOUT: %bound_method.loc16: = bound_method %.loc16_25, %F.ref.loc16 +// CHECK:STDOUT: %WithRefQualifier.F.call: init %ptr.235 = call imports.%WithRefQualifier.F.decl.e3f32b.2(%.loc16_25) +// CHECK:STDOUT: assign %b.var, %WithRefQualifier.F.call +// CHECK:STDOUT: %.loc16_20: type = splice_block %ptr.loc16 [concrete = constants.%ptr.235] { +// CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %ptr.loc16: type = ptr_type %i32.loc16 [concrete = constants.%ptr.235] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: ref %ptr.235 = ref_binding b, %b.var +// CHECK:STDOUT: %Destroy.Op.bound.loc16: = bound_method %b.var, constants.%Destroy.Op.651ba6.1 +// CHECK:STDOUT: %Destroy.Op.call.loc16: init %empty_tuple.type = call %Destroy.Op.bound.loc16(%b.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc15: = bound_method %a.var, constants.%Destroy.Op.651ba6.2 +// CHECK:STDOUT: %Destroy.Op.call.loc15: init %empty_tuple.type = call %Destroy.Op.bound.loc15(%a.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- call_explicit_object_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/check/testdata/interop/cpp/function/operators.carbon b/toolchain/check/testdata/interop/cpp/function/operators.carbon index 2026172bf78e..4f80606cd14c 100644 --- a/toolchain/check/testdata/interop/cpp/function/operators.carbon +++ b/toolchain/check/testdata/interop/cpp/function/operators.carbon @@ -283,7 +283,7 @@ void operator+(C, short); '''; fn Test(c: Cpp.C) { - // CHECK:STDERR: fail_ambiguous.carbon:[[@LINE+10]]:5: error: use of overloaded operator '+' is ambiguous (with operand types 'C' and 'int') [CppInteropParseError] + // CHECK:STDERR: fail_ambiguous.carbon:[[@LINE+10]]:5: error: use of overloaded operator '+' is ambiguous (with operand types 'const C' and 'int') [CppInteropParseError] // CHECK:STDERR: 21 | c + 1; // CHECK:STDERR: | ^ // CHECK:STDERR: fail_ambiguous.carbon:[[@LINE-8]]:6: note: candidate function [CppInteropParseNote] diff --git a/toolchain/check/testdata/interop/cpp/function/reference.carbon b/toolchain/check/testdata/interop/cpp/function/reference.carbon index d3bad6dda367..9fd4480d5aa6 100644 --- a/toolchain/check/testdata/interop/cpp/function/reference.carbon +++ b/toolchain/check/testdata/interop/cpp/function/reference.carbon @@ -48,7 +48,7 @@ fn F() { // CHECK:STDERR: 18 | Cpp.TakesLValue(s); // CHECK:STDERR: | ^ // CHECK:STDERR: fail_param_lvalue_ref.carbon:[[@LINE-9]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./param_lvalue_ref.h:5:6: note: candidate function not viable: expects an lvalue for 1st argument [CppInteropParseNote] + // CHECK:STDERR: ./param_lvalue_ref.h:5:6: note: candidate function not viable: 1st argument ('const S') would lose const qualifier [CppInteropParseNote] // CHECK:STDERR: 5 | auto TakesLValue(S&) -> void; // CHECK:STDERR: | ^ ~~ // CHECK:STDERR: @@ -111,21 +111,23 @@ fn F() { //@dump-sem-ir-end } -// --- todo_fail_param_value_arg_for_rvalue_ref.carbon +// --- fail_param_value_arg_for_rvalue_ref.carbon library "[[@TEST_NAME]]"; import Cpp library "param_rvalue_ref.h"; fn F() { - //@dump-sem-ir-begin - // TODO: We should probably reject binding an rvalue reference to a value - // expression. If we don't reject, we should instead force a copy to be made, - // at least if the type has a pointer value representation, so that moving - // from the reference doesn't alter tne original value. let s: Cpp.S = {}; + // CHECK:STDERR: fail_param_value_arg_for_rvalue_ref.carbon:[[@LINE+8]]:20: error: no matching function for call to 'TakesRValue' [CppInteropParseError] + // CHECK:STDERR: 16 | Cpp.TakesRValue(s); + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_param_value_arg_for_rvalue_ref.carbon:[[@LINE-7]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./param_rvalue_ref.h:5:6: note: candidate function not viable: 1st argument ('const S') would lose const qualifier [CppInteropParseNote] + // CHECK:STDERR: 5 | auto TakesRValue(S&&) -> void; + // CHECK:STDERR: | ^ ~~~ + // CHECK:STDERR: Cpp.TakesRValue(s); - //@dump-sem-ir-end } // --- fail_param_rvalue_ref.carbon @@ -526,66 +528,6 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- todo_fail_param_value_arg_for_rvalue_ref.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %S: type = class_type @S [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %pattern_type.7da: type = pattern_type %S [concrete] -// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] -// CHECK:STDOUT: %S.val: %S = struct_value () [concrete] -// CHECK:STDOUT: %TakesRValue.cpp_overload_set.type: type = cpp_overload_set_type @TakesRValue.cpp_overload_set [concrete] -// CHECK:STDOUT: %TakesRValue.cpp_overload_set.value: %TakesRValue.cpp_overload_set.type = cpp_overload_set_value @TakesRValue.cpp_overload_set [concrete] -// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete] -// CHECK:STDOUT: %TakesRValue__carbon_thunk.type: type = fn_type @TakesRValue__carbon_thunk [concrete] -// CHECK:STDOUT: %TakesRValue__carbon_thunk: %TakesRValue__carbon_thunk.type = struct_value () [concrete] -// CHECK:STDOUT: %S.cpp_destructor.type: type = fn_type @S.cpp_destructor [concrete] -// CHECK:STDOUT: %S.cpp_destructor: %S.cpp_destructor.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { -// CHECK:STDOUT: .S = %S.decl -// CHECK:STDOUT: .TakesRValue = %TakesRValue.cpp_overload_set.value -// CHECK:STDOUT: import Cpp//... -// CHECK:STDOUT: } -// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {} -// CHECK:STDOUT: %TakesRValue.cpp_overload_set.value: %TakesRValue.cpp_overload_set.type = cpp_overload_set_value @TakesRValue.cpp_overload_set [concrete = constants.%TakesRValue.cpp_overload_set.value] -// CHECK:STDOUT: %TakesRValue__carbon_thunk.decl: %TakesRValue__carbon_thunk.type = fn_decl @TakesRValue__carbon_thunk [concrete = constants.%TakesRValue__carbon_thunk] { -// CHECK:STDOUT: -// CHECK:STDOUT: } { -// CHECK:STDOUT: -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %s.patt: %pattern_type.7da = value_binding_pattern s [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: %.loc12_19.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc12_13: type = splice_block %S.ref [concrete = constants.%S] { -// CHECK:STDOUT: %Cpp.ref.loc12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S] -// CHECK:STDOUT: } -// CHECK:STDOUT: %.loc12_19.2: ref %S = temporary_storage -// CHECK:STDOUT: %.loc12_19.3: init %S to %.loc12_19.2 = class_init () [concrete = constants.%S.val] -// CHECK:STDOUT: %.loc12_19.4: ref %S = temporary %.loc12_19.2, %.loc12_19.3 -// CHECK:STDOUT: %.loc12_19.5: ref %S = converted %.loc12_19.1, %.loc12_19.4 -// CHECK:STDOUT: %.loc12_19.6: %S = acquire_value %.loc12_19.5 -// CHECK:STDOUT: %s: %S = value_binding s, %.loc12_19.6 -// CHECK:STDOUT: %Cpp.ref.loc13: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %TakesRValue.ref: %TakesRValue.cpp_overload_set.type = name_ref TakesRValue, imports.%TakesRValue.cpp_overload_set.value [concrete = constants.%TakesRValue.cpp_overload_set.value] -// CHECK:STDOUT: %s.ref: %S = name_ref s, %s -// CHECK:STDOUT: %.loc13: ref %S = value_as_ref %s.ref -// CHECK:STDOUT: %addr: %ptr.5c7 = addr_of %.loc13 -// CHECK:STDOUT: %TakesRValue__carbon_thunk.call: init %empty_tuple.type = call imports.%TakesRValue__carbon_thunk.decl(%addr) -// CHECK:STDOUT: %S.cpp_destructor.bound: = bound_method %.loc12_19.4, constants.%S.cpp_destructor -// CHECK:STDOUT: %S.cpp_destructor.call: init %empty_tuple.type = call %S.cpp_destructor.bound(%.loc12_19.4) -// CHECK:STDOUT: -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: --- fail_param_rvalue_ref.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/check/testdata/interop/cpp/impls/implicit_as.carbon b/toolchain/check/testdata/interop/cpp/impls/implicit_as.carbon index 1378a9915dcb..677587872b6e 100644 --- a/toolchain/check/testdata/interop/cpp/impls/implicit_as.carbon +++ b/toolchain/check/testdata/interop/cpp/impls/implicit_as.carbon @@ -92,10 +92,10 @@ import Cpp library "implicit_conversions.h"; fn NonConstConversionTest(s: Cpp.NonConstConversion) { //@dump-sem-ir-begin - // CHECK:STDERR: fail_expr_category.carbon:[[@LINE+7]]:21: error: value expression passed to reference parameter [ValueForRefParam] + // CHECK:STDERR: fail_expr_category.carbon:[[@LINE+7]]:21: error: cannot implicitly convert expression of type `Cpp.NonConstConversion` to `Cpp.Dest` [ConversionFailure] // CHECK:STDERR: let _: Cpp.Dest = s; // CHECK:STDERR: ^ - // CHECK:STDERR: fail_expr_category.carbon:[[@LINE+4]]:21: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: fail_expr_category.carbon:[[@LINE+4]]:21: note: type `Cpp.NonConstConversion` does not implement interface `Core.ImplicitAs(Cpp.Dest)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let _: Cpp.Dest = s; // CHECK:STDERR: ^ // CHECK:STDERR: @@ -568,18 +568,9 @@ fn InitFromStruct() { // CHECK:STDOUT: --- fail_expr_category.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %NonConstConversion: type = class_type @NonConstConversion [concrete] -// CHECK:STDOUT: %pattern_type.b08: type = pattern_type %NonConstConversion [concrete] -// CHECK:STDOUT: %Dest: type = class_type @Dest [concrete] -// CHECK:STDOUT: %pattern_type.69a: type = pattern_type %Dest [concrete] -// CHECK:STDOUT: %NonConstConversion.cpp_operator.type: type = fn_type @NonConstConversion.cpp_operator [concrete] -// CHECK:STDOUT: %NonConstConversion.cpp_operator: %NonConstConversion.cpp_operator.type = struct_value () [concrete] -// CHECK:STDOUT: %ptr.551: type = ptr_type %Dest [concrete] -// CHECK:STDOUT: %Dest__carbon_thunk.type: type = fn_type @Dest__carbon_thunk [concrete] -// CHECK:STDOUT: %Dest__carbon_thunk: %Dest__carbon_thunk.type = struct_value () [concrete] -// CHECK:STDOUT: %Dest.cpp_destructor.type: type = fn_type @Dest.cpp_destructor [concrete] -// CHECK:STDOUT: %Dest.cpp_destructor: %Dest.cpp_destructor.type = struct_value () [concrete] +// CHECK:STDOUT: %Dest.5e7: type = class_type @Dest [concrete] +// CHECK:STDOUT: %pattern_type.69a: type = pattern_type %Dest.5e7 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -589,21 +580,7 @@ fn InitFromStruct() { // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } // CHECK:STDOUT: %NonConstConversion.decl: type = class_decl @NonConstConversion [concrete = constants.%NonConstConversion] {} {} -// CHECK:STDOUT: %Dest.decl: type = class_decl @Dest [concrete = constants.%Dest] {} {} -// CHECK:STDOUT: %NonConstConversion.cpp_operator.decl: %NonConstConversion.cpp_operator.type = fn_decl @NonConstConversion.cpp_operator [concrete = constants.%NonConstConversion.cpp_operator] { -// CHECK:STDOUT: %self.patt: %pattern_type.b08 = ref_binding_pattern self [concrete] -// CHECK:STDOUT: %self.param_patt: %pattern_type.b08 = ref_param_pattern %self.patt [concrete] -// CHECK:STDOUT: -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: ref %NonConstConversion = ref_param call_param0 -// CHECK:STDOUT: %self: ref %NonConstConversion = ref_binding self, %self.param -// CHECK:STDOUT: -// CHECK:STDOUT: } -// CHECK:STDOUT: %Dest__carbon_thunk.decl: %Dest__carbon_thunk.type = fn_decl @Dest__carbon_thunk [concrete = constants.%Dest__carbon_thunk] { -// CHECK:STDOUT: -// CHECK:STDOUT: } { -// CHECK:STDOUT: -// CHECK:STDOUT: } +// CHECK:STDOUT: %Dest.decl: type = class_decl @Dest [concrete = constants.%Dest.5e7] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @NonConstConversionTest(%s.param: %NonConstConversion) { @@ -612,21 +589,12 @@ fn InitFromStruct() { // CHECK:STDOUT: %_.patt: %pattern_type.69a = value_binding_pattern _ [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %s.ref: %NonConstConversion = name_ref s, %s -// CHECK:STDOUT: %.loc15_13: type = splice_block %Dest.ref [concrete = constants.%Dest] { +// CHECK:STDOUT: %.loc15_13: type = splice_block %Dest.ref [concrete = constants.%Dest.5e7] { // CHECK:STDOUT: %Cpp.ref.loc15: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Dest.ref: type = name_ref Dest, imports.%Dest.decl [concrete = constants.%Dest] +// CHECK:STDOUT: %Dest.ref: type = name_ref Dest, imports.%Dest.decl [concrete = constants.%Dest.5e7] // CHECK:STDOUT: } -// CHECK:STDOUT: %NonConstConversion.cpp_operator.bound: = bound_method %s.ref, imports.%NonConstConversion.cpp_operator.decl -// CHECK:STDOUT: %.loc15_21.1: ref %Dest = temporary_storage -// CHECK:STDOUT: %addr: %ptr.551 = addr_of %.loc15_21.1 -// CHECK:STDOUT: %Dest__carbon_thunk.call: init %empty_tuple.type = call imports.%Dest__carbon_thunk.decl(, %addr) -// CHECK:STDOUT: %.loc15_21.2: init %Dest to %.loc15_21.1 = mark_in_place_init %Dest__carbon_thunk.call -// CHECK:STDOUT: %.loc15_21.3: init %Dest = converted %s.ref, %.loc15_21.2 -// CHECK:STDOUT: %.loc15_21.4: ref %Dest = temporary %.loc15_21.1, %.loc15_21.3 -// CHECK:STDOUT: %.loc15_21.5: %Dest = acquire_value %.loc15_21.4 -// CHECK:STDOUT: %_: %Dest = value_binding _, %.loc15_21.5 -// CHECK:STDOUT: %Dest.cpp_destructor.bound: = bound_method %.loc15_21.4, constants.%Dest.cpp_destructor -// CHECK:STDOUT: %Dest.cpp_destructor.call: init %empty_tuple.type = call %Dest.cpp_destructor.bound(%.loc15_21.4) +// CHECK:STDOUT: %.loc15_21: %Dest.5e7 = converted %s.ref, [concrete = ] +// CHECK:STDOUT: %_: %Dest.5e7 = value_binding _, [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: