From d85781acbf9d34382d66e09c774cf033df28365e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 24 Sep 2025 21:19:18 -0700 Subject: [PATCH] Fix handling of deleted and templated constructors. (#6129) Don't ignore deleted constructors in overload resolution. If one is the best match, we want an error rather than picking something else. Don't crash if we find a constructor template or other weird thing; use `getConstructorInfo` to map it into a constructor and skip it if it isn't one, like Clang does. --- toolchain/check/cpp/import.cpp | 9 +- .../interop/cpp/class/constructor.carbon | 242 +++++++++++++++++- 2 files changed, 240 insertions(+), 11 deletions(-) diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 6e8a2bf1eb07..7a794f918ac2 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -21,6 +21,7 @@ #include "clang/Frontend/TextDiagnostic.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" #include "common/check.h" #include "common/ostream.h" #include "common/raw_string_ostream.h" @@ -2117,12 +2118,12 @@ static auto ImportConstructorsIntoScope(Context& context, SemIR::LocId loc_id, ClangConstructorLookup(context, scope_id); clang::UnresolvedSet<4> overload_set; - for (clang::Decl* decl : constructors_lookup) { - auto* constructor = cast(decl); - if (constructor->isDeleted() || constructor->isCopyOrMoveConstructor()) { + for (auto* decl : constructors_lookup) { + auto info = clang::getConstructorInfo(decl); + if (!info.Constructor || info.Constructor->isCopyOrMoveConstructor()) { continue; } - overload_set.addDecl(constructor, constructor->getAccess()); + overload_set.addDecl(info.FoundDecl, info.FoundDecl->getAccess()); } if (overload_set.empty()) { return SemIR::ScopeLookupResult::MakeNotFound(); diff --git a/toolchain/check/testdata/interop/cpp/class/constructor.carbon b/toolchain/check/testdata/interop/cpp/class/constructor.carbon index 6a14e24933b7..6ad783cdd355 100644 --- a/toolchain/check/testdata/interop/cpp/class/constructor.carbon +++ b/toolchain/check/testdata/interop/cpp/class/constructor.carbon @@ -106,28 +106,78 @@ fn F() { } // ============================================================================ -// No constructors +// Constructor templates // ============================================================================ -// --- none.h +// --- template.h + +class C { + public: + C(int); + template C(T); +}; + +// --- import_template.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "template.h"; + +fn F() { + //@dump-sem-ir-begin + var c1: Cpp.C = Cpp.C.C(123); + var c2: Cpp.C = Cpp.C.C(true); + var c3: Cpp.C = Cpp.C.C(&c1); + //@dump-sem-ir-end +} + +// ============================================================================ +// Deleted constructor +// ============================================================================ + +// --- deleted.h class C { public: C() = delete; }; -// --- fail_import_none.carbon +class D { + public: + D(int) = delete; + D(short); +}; + +// --- fail_import_deleted.carbon library "[[@TEST_NAME]]"; -import Cpp library "none.h"; +import Cpp library "deleted.h"; fn F() { - // CHECK:STDERR: fail_import_none.carbon:[[@LINE+4]]:18: error: member name `C` not found in `Cpp.C` [MemberNameNotFoundInInstScope] - // CHECK:STDERR: let c: Cpp.C = Cpp.C.C(); - // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_deleted.carbon:[[@LINE+8]]:26: error: call to deleted function 'C' [CppInteropParseError] + // CHECK:STDERR: 15 | let c: Cpp.C = Cpp.C.C(); + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_import_deleted.carbon:[[@LINE-6]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./deleted.h:4:3: note: candidate constructor has been explicitly deleted [CppInteropParseNote] + // CHECK:STDERR: 4 | C() = delete; + // CHECK:STDERR: | ^ // CHECK:STDERR: let c: Cpp.C = Cpp.C.C(); + + // CHECK:STDERR: fail_import_deleted.carbon:[[@LINE+12]]:27: error: call to deleted function 'D' [CppInteropParseError] + // CHECK:STDERR: 29 | let d: Cpp.D = Cpp.D.D(0); + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_import_deleted.carbon:[[@LINE-16]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./deleted.h:9:3: note: candidate constructor has been explicitly deleted [CppInteropParseNote] + // CHECK:STDERR: 9 | D(int) = delete; + // CHECK:STDERR: | ^ + // CHECK:STDERR: fail_import_deleted.carbon:[[@LINE-20]]:10: in file included here [InCppInclude] + // CHECK:STDERR: ./deleted.h:10:3: note: candidate constructor [CppInteropParseNote] + // CHECK:STDERR: 10 | D(short); + // CHECK:STDERR: | ^ + // CHECK:STDERR: + let d: Cpp.D = Cpp.D.D(0); } // ============================================================================ @@ -646,6 +696,184 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- import_template.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %pattern_type.217: type = pattern_type %C [concrete] +// CHECK:STDOUT: %.d40: type = cpp_overload_set_type @C.C.1 [concrete] +// CHECK:STDOUT: %empty_struct: %.d40 = struct_value () [concrete] +// CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type.65f120.1: type = fn_type @C__carbon_thunk.1 [concrete] +// CHECK:STDOUT: %C__carbon_thunk.d98342.1: %C__carbon_thunk.type.65f120.1 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.204: = impl_witness imports.%ImplicitAs.impl_witness_table.9e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.d14 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.204) [concrete] +// CHECK:STDOUT: %.1df: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_123.f7f: %i32 = int_value 123 [concrete] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete] +// CHECK:STDOUT: %ptr.bb2: type = ptr_type bool [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type.65f120.2: type = fn_type @C__carbon_thunk.2 [concrete] +// CHECK:STDOUT: %C__carbon_thunk.d98342.2: %C__carbon_thunk.type.65f120.2 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %Copy.Op.type: type = fn_type @Copy.Op [concrete] +// CHECK:STDOUT: %Copy.impl_witness.a56: = impl_witness imports.%Copy.impl_witness_table.189 [concrete] +// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value bool, (%Copy.impl_witness.a56) [concrete] +// CHECK:STDOUT: %.05c: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet [concrete] +// CHECK:STDOUT: %bool.as.Copy.impl.Op.type: type = fn_type @bool.as.Copy.impl.Op [concrete] +// CHECK:STDOUT: %bool.as.Copy.impl.Op: %bool.as.Copy.impl.Op.type = struct_value () [concrete] +// CHECK:STDOUT: %bool.as.Copy.impl.Op.bound: = bound_method %true, %bool.as.Copy.impl.Op [concrete] +// CHECK:STDOUT: %C__carbon_thunk.type.65f120.3: type = fn_type @C__carbon_thunk.3 [concrete] +// CHECK:STDOUT: %C__carbon_thunk.d98342.3: %C__carbon_thunk.type.65f120.3 = struct_value () [concrete] +// CHECK:STDOUT: %type_where: type = facet_type > [concrete] +// CHECK:STDOUT: %facet_value: %type_where = facet_value %C, () [concrete] +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.type.fc1: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete] +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.6b9: %AggregateT.as_type.as.Destroy.impl.Op.type.fc1 = struct_value () [concrete] +// CHECK:STDOUT: %bool.as.Destroy.impl.Op.type: type = fn_type @bool.as.Destroy.impl.Op [concrete] +// CHECK:STDOUT: %bool.as.Destroy.impl.Op: %bool.as.Destroy.impl.Op.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = 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: %.40b: %.d40 = cpp_overload_set_value @C.C.1 [concrete = constants.%empty_struct] +// CHECK:STDOUT: %C__carbon_thunk.decl.8acdfe.1: %C__carbon_thunk.type.65f120.1 = fn_decl @C__carbon_thunk.1 [concrete = constants.%C__carbon_thunk.d98342.1] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: %C__carbon_thunk.decl.8acdfe.2: %C__carbon_thunk.type.65f120.2 = fn_decl @C__carbon_thunk.2 [concrete = constants.%C__carbon_thunk.d98342.2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import_ref.cc5: %bool.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc20_31, loaded [concrete = constants.%bool.as.Copy.impl.Op] +// CHECK:STDOUT: %Copy.impl_witness_table.189 = impl_witness_table (%Core.import_ref.cc5), @bool.as.Copy.impl [concrete] +// CHECK:STDOUT: %C__carbon_thunk.decl.8acdfe.3: %C__carbon_thunk.type.65f120.3 = fn_decl @C__carbon_thunk.3 [concrete = constants.%C__carbon_thunk.d98342.3] { +// 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: %c1.patt: %pattern_type.217 = binding_pattern c1 [concrete] +// CHECK:STDOUT: %c1.var_patt: %pattern_type.217 = var_pattern %c1.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1.var: ref %C = var %c1.var_patt +// CHECK:STDOUT: %Cpp.ref.loc8_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc8_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc8_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct] +// CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff] +// CHECK:STDOUT: %.loc8_3.1: ref %C = splice_block %c1.var {} +// CHECK:STDOUT: %impl.elem0.loc8: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0] +// CHECK:STDOUT: %bound_method.loc8_27.1: = bound_method %int_123, %impl.elem0.loc8 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0.loc8, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc8_27.2: = bound_method %int_123, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_27.2(%int_123) [concrete = constants.%int_123.f7f] +// CHECK:STDOUT: %.loc8_27.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.f7f] +// CHECK:STDOUT: %.loc8_27.2: %i32 = converted %int_123, %.loc8_27.1 [concrete = constants.%int_123.f7f] +// CHECK:STDOUT: %addr.loc8_30: %ptr.d9e = addr_of %.loc8_3.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.1(%.loc8_27.2, %addr.loc8_30) +// CHECK:STDOUT: %.loc8_30: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_3.1 +// CHECK:STDOUT: assign %c1.var, %.loc8_30 +// CHECK:STDOUT: %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c1: ref %C = bind_name c1, %c1.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete] +// CHECK:STDOUT: %c2.var_patt: %pattern_type.217 = var_pattern %c2.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c2.var: ref %C = var %c2.var_patt +// CHECK:STDOUT: %Cpp.ref.loc9_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc9_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] +// CHECK:STDOUT: %.loc9_3.1: ref %C = splice_block %c2.var {} +// CHECK:STDOUT: %.loc9_27.1: ref bool = temporary_storage +// CHECK:STDOUT: %impl.elem0.loc9: %.05c = impl_witness_access constants.%Copy.impl_witness.a56, element0 [concrete = constants.%bool.as.Copy.impl.Op] +// CHECK:STDOUT: %bound_method.loc9_27: = bound_method %true, %impl.elem0.loc9 [concrete = constants.%bool.as.Copy.impl.Op.bound] +// CHECK:STDOUT: %bool.as.Copy.impl.Op.call: init bool = call %bound_method.loc9_27(%true) [concrete = constants.%true] +// CHECK:STDOUT: %.loc9_27.2: ref bool = temporary %.loc9_27.1, %bool.as.Copy.impl.Op.call +// CHECK:STDOUT: %addr.loc9_31.1: %ptr.bb2 = addr_of %.loc9_27.2 +// CHECK:STDOUT: %addr.loc9_31.2: %ptr.d9e = addr_of %.loc9_3.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.2(%addr.loc9_31.1, %addr.loc9_31.2) +// CHECK:STDOUT: %.loc9_31: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_3.1 +// CHECK:STDOUT: assign %c2.var, %.loc9_31 +// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc9_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c2: ref %C = bind_name c2, %c2.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c3.patt: %pattern_type.217 = binding_pattern c3 [concrete] +// CHECK:STDOUT: %c3.var_patt: %pattern_type.217 = var_pattern %c3.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c3.var: ref %C = var %c3.var_patt +// CHECK:STDOUT: %Cpp.ref.loc10_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc10_22: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc10_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct] +// CHECK:STDOUT: %c1.ref: ref %C = name_ref c1, %c1 +// CHECK:STDOUT: %addr.loc10_27: %ptr.d9e = addr_of %c1.ref +// CHECK:STDOUT: %.loc10_3.1: ref %C = splice_block %c3.var {} +// CHECK:STDOUT: %addr.loc10_30: %ptr.d9e = addr_of %.loc10_3.1 +// CHECK:STDOUT: %C__carbon_thunk.call.loc10: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.3(%addr.loc10_27, %addr.loc10_30) +// CHECK:STDOUT: %.loc10_30: init %C = in_place_init %C__carbon_thunk.call.loc10, %.loc10_3.1 +// CHECK:STDOUT: assign %c3.var, %.loc10_30 +// CHECK:STDOUT: %.loc10_14: type = splice_block %C.ref.loc10_14 [concrete = constants.%C] { +// CHECK:STDOUT: %Cpp.ref.loc10_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc10_14: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c3: ref %C = bind_name c3, %c3.var +// CHECK:STDOUT: %facet_value.loc10: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value] +// CHECK:STDOUT: %.loc10_3.2: %type_where = converted constants.%C, %facet_value.loc10 [concrete = constants.%facet_value] +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc10: = bound_method %c3.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc10: = bound_method %c3.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc10_3: %ptr.d9e = addr_of %c3.var +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_3) +// CHECK:STDOUT: %bool.as.Destroy.impl.Op.bound: = bound_method %.loc9_27.2, constants.%bool.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc9_27: %ptr.bb2 = addr_of %.loc9_27.2 +// CHECK:STDOUT: %bool.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bool.as.Destroy.impl.Op.bound(%addr.loc9_27) +// CHECK:STDOUT: %facet_value.loc9: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value] +// CHECK:STDOUT: %.loc9_3.2: %type_where = converted constants.%C, %facet_value.loc9 [concrete = constants.%facet_value] +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc9: = bound_method %c2.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9_3: = bound_method %c2.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc9_3: %ptr.d9e = addr_of %c2.var +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9_3(%addr.loc9_3) +// CHECK:STDOUT: %facet_value.loc8: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value] +// CHECK:STDOUT: %.loc8_3.2: %type_where = converted constants.%C, %facet_value.loc8 [concrete = constants.%facet_value] +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: = bound_method %c1.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8_3: = bound_method %c1.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc8_3: %ptr.d9e = addr_of %c1.var +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8_3(%addr.loc8_3) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_import_implicit_single_argument.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants {