From 3b6d202730b817f5b5d55da9dda19d4c07239740 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 26 Sep 2025 16:18:16 -0700 Subject: [PATCH] Implement support for mixed-access overload sets. (#6137) This turns out to be quite important, as several important standard library types (such as `std::string`) have mixed-access overload sets for their constructors as an implementation detail. The overall approach here is: - Use the most permissive access to determine the access of the overload set itself. This affects whether name lookup finds the member name at all. - After overload resolution, re-check the access of the selected member, if it's protected or private. --------- Co-authored-by: Dana Jansens --- toolchain/check/cpp/import.cpp | 79 +++--- toolchain/check/cpp/import.h | 3 +- toolchain/check/cpp/operators.cpp | 3 +- toolchain/check/cpp/overload_resolution.cpp | 43 ++- toolchain/check/member_access.cpp | 6 +- toolchain/check/member_access.h | 7 + toolchain/check/name_lookup.cpp | 21 +- toolchain/check/name_lookup.h | 7 + .../testdata/interop/cpp/class/access.carbon | 149 +++++++++-- .../interop/cpp/function/overloads.carbon | 249 ------------------ toolchain/sem_ir/cpp_overload_set.h | 3 + 11 files changed, 238 insertions(+), 332 deletions(-) diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index ae482af8e6ed..67636f26e47f 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -508,18 +508,6 @@ static auto GetDeclContext(Context& context, SemIR::NameScopeId scope_id) context.clang_decls().Get(scope_clang_decl_context_id).key.decl); } -// Looks up for constructors in the class scope and returns the lookup result. -static auto ClangConstructorLookup(Context& context, - SemIR::NameScopeId scope_id) - -> clang::DeclContextLookupResult { - const SemIR::NameScope& scope = context.name_scopes().Get(scope_id); - - clang::Sema& sema = context.clang_sema(); - clang::Decl* decl = - context.clang_decls().Get(scope.clang_decl_context_id()).key.decl; - return sema.LookupConstructors(cast(decl)); -} - // Returns true if the given Clang declaration is the implicit injected class // name within the class. static auto IsDeclInjectedClassName(Context& context, @@ -2074,12 +2062,14 @@ static auto LookupBuiltinTypes(Context& context, SemIR::LocId loc_id, auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id, - const clang::UnresolvedSet<4>& overload_set) + clang::CXXRecordDecl* naming_class, + clang::UnresolvedSet<4>&& overload_set) -> SemIR::InstId { SemIR::CppOverloadSetId overload_set_id = context.cpp_overload_sets().Add( SemIR::CppOverloadSet{.name_id = name_id, .parent_scope_id = scope_id, - .candidate_functions = overload_set}); + .naming_class = naming_class, + .candidate_functions = std::move(overload_set)}); auto overload_set_inst_id = // TODO: Add a location. @@ -2093,17 +2083,18 @@ auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id, return overload_set_inst_id; } -// Gets the access for an overloaded function set. Returns std::nullopt -// if the access is not the same for all functions in the overload set. -// TODO: Fix to support functions with different access levels. -static auto GetOverloadSetAccess(Context& context, SemIR::LocId loc_id, - const clang::UnresolvedSet<4>& overload_set) - -> std::optional { +// Gets the best access for an overloaded function set. This is the access that +// we use for the overload set as a whole. More fine-grained checking is done +// after overload resolution. +static auto GetOverloadSetAccess(const clang::UnresolvedSet<4>& overload_set) + -> SemIR::AccessKind { clang::AccessSpecifier access = overload_set.begin().getAccess(); for (auto it = overload_set.begin() + 1; it != overload_set.end(); ++it) { - if (it.getAccess() != access) { - context.TODO(loc_id, "Unsupported: Overloaded set with mixed access"); - return std::nullopt; + CARBON_CHECK( + (it.getAccess() == clang::AS_none) == (access == clang::AS_none), + "Unexpected mixture of members and non-members"); + if (it.getAccess() < access) { + access = it->getAccess(); } } return MapAccess(access); @@ -2111,32 +2102,31 @@ static auto GetOverloadSetAccess(Context& context, SemIR::LocId loc_id, // Imports an overload set from Clang to Carbon and adds the name into the // `NameScope`. -static auto ImportOverloadSetIntoScope( - Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, - SemIR::NameId name_id, const clang::UnresolvedSet<4>& overload_set) +static auto ImportOverloadSetIntoScope(Context& context, + SemIR::NameScopeId scope_id, + SemIR::NameId name_id, + clang::CXXRecordDecl* naming_class, + clang::UnresolvedSet<4>&& overload_set) -> SemIR::ScopeLookupResult { - std::optional access_kind = - GetOverloadSetAccess(context, loc_id, overload_set); - if (!access_kind.has_value()) { - return SemIR::ScopeLookupResult::MakeError(); - } - - SemIR::InstId inst_id = - ImportCppOverloadSet(context, scope_id, name_id, overload_set); - AddNameToScope(context, scope_id, name_id, access_kind.value(), inst_id); + SemIR::AccessKind access_kind = GetOverloadSetAccess(overload_set); + SemIR::InstId inst_id = ImportCppOverloadSet( + context, scope_id, name_id, naming_class, std::move(overload_set)); + AddNameToScope(context, scope_id, name_id, access_kind, inst_id); return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id, - access_kind.value()); + access_kind); } // Imports the constructors for a given class name. The found constructors are // imported as part of an overload set into the scope. Currently copy/move // constructors are not imported. -static auto ImportConstructorsIntoScope(Context& context, SemIR::LocId loc_id, +static auto ImportConstructorsIntoScope(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult { + auto* naming_class = + cast(GetDeclContext(context, scope_id)); clang::DeclContextLookupResult constructors_lookup = - ClangConstructorLookup(context, scope_id); + context.clang_sema().LookupConstructors(naming_class); clang::UnresolvedSet<4> overload_set; for (auto* decl : constructors_lookup) { @@ -2144,14 +2134,14 @@ static auto ImportConstructorsIntoScope(Context& context, SemIR::LocId loc_id, if (!info.Constructor || info.Constructor->isCopyOrMoveConstructor()) { continue; } - overload_set.addDecl(info.FoundDecl, info.FoundDecl->getAccess()); + overload_set.addDecl(info.FoundDecl, info.FoundDecl.getAccess()); } if (overload_set.empty()) { return SemIR::ScopeLookupResult::MakeNotFound(); } - return ImportOverloadSetIntoScope(context, loc_id, scope_id, name_id, - overload_set); + return ImportOverloadSetIntoScope(context, scope_id, name_id, naming_class, + std::move(overload_set)); } // Imports a builtin type from Clang to Carbon and adds the name into the @@ -2207,8 +2197,9 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, lookup->getFoundDecl()->isFunctionOrFunctionTemplate())) { clang::UnresolvedSet<4> overload_set; overload_set.append(lookup->begin(), lookup->end()); - return ImportOverloadSetIntoScope(context, loc_id, scope_id, name_id, - overload_set); + return ImportOverloadSetIntoScope(context, scope_id, name_id, + lookup->getNamingClass(), + std::move(overload_set)); } if (!lookup->isSingleResult()) { // Clang will diagnose ambiguous lookup results for us. @@ -2224,7 +2215,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, } if (IsDeclInjectedClassName(context, scope_id, name_id, lookup->getFoundDecl())) { - return ImportConstructorsIntoScope(context, loc_id, scope_id, name_id); + return ImportConstructorsIntoScope(context, scope_id, name_id); } auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(lookup->getFoundDecl()); return ImportNameDeclIntoScope(context, loc_id, scope_id, name_id, key, diff --git a/toolchain/check/cpp/import.h b/toolchain/check/cpp/import.h index 805f8b4b49ac..85795e5c26f6 100644 --- a/toolchain/check/cpp/import.h +++ b/toolchain/check/cpp/import.h @@ -34,7 +34,8 @@ auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id, // Imports an overloaded function set from Clang to Carbon. auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id, - const clang::UnresolvedSet<4>& overload_set) + clang::CXXRecordDecl* naming_class, + clang::UnresolvedSet<4>&& overload_set) -> SemIR::InstId; // Looks up the given name in the Clang AST generated when importing C++ code diff --git a/toolchain/check/cpp/operators.cpp b/toolchain/check/cpp/operators.cpp index 5369b1bb74cb..b848ba843ef4 100644 --- a/toolchain/check/cpp/operators.cpp +++ b/toolchain/check/cpp/operators.cpp @@ -206,7 +206,8 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op, } return ImportCppOverloadSet(context, SemIR::NameScopeId::None, - SemIR::NameId::CppOperator, functions); + SemIR::NameId::CppOperator, + /*naming_class=*/nullptr, std::move(functions)); } } // namespace Carbon::Check diff --git a/toolchain/check/cpp/overload_resolution.cpp b/toolchain/check/cpp/overload_resolution.cpp index b05fa0429ba7..2dd36addd3b2 100644 --- a/toolchain/check/cpp/overload_resolution.cpp +++ b/toolchain/check/cpp/overload_resolution.cpp @@ -11,9 +11,12 @@ #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/type_mapping.h" +#include "toolchain/check/member_access.h" +#include "toolchain/check/name_lookup.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -79,6 +82,40 @@ static auto AddOverloadCandidataes(clang::Sema& sema, } } +// Checks whether a selected overload is accessible and diagnoses if not. +static auto CheckOverloadAccess(Context& context, SemIR::LocId loc_id, + const SemIR::CppOverloadSet& overload_set, + clang::DeclAccessPair overload, + SemIR::InstId overload_inst_id) -> void { + SemIR::AccessKind member_access_kind; + switch (overload->getAccess()) { + case clang::AS_none: + case clang::AS_public: { + return; + } + + case clang::AS_protected: { + member_access_kind = SemIR::AccessKind::Protected; + break; + } + + case clang::AS_private: { + member_access_kind = SemIR::AccessKind::Private; + break; + } + } + + auto name_scope_const_id = context.constant_values().Get( + context.name_scopes().Get(overload_set.parent_scope_id).inst_id()); + SemIR::AccessKind allowed_access_kind = + GetHighestAllowedAccess(context, loc_id, name_scope_const_id); + CheckAccess(context, loc_id, SemIR::LocId(overload_inst_id), + overload_set.name_id, member_access_kind, + /*is_parent_access=*/false, + {.constant_id = name_scope_const_id, + .highest_allowed_access = allowed_access_kind}); +} + // Returns whether the decl is an operator member function. static auto IsOperatorMethodDecl(clang::Decl* decl) -> bool { auto* clang_method_decl = dyn_cast(decl); @@ -136,12 +173,14 @@ static auto ResolveCalleeId(Context& context, SemIR::LocId loc_id, // TODO: Handle the cases when Function is null. CARBON_CHECK(best_viable_fn->Function); sema.MarkFunctionReferenced(loc, best_viable_fn->Function); - SemIR::InstId result = ImportCppFunctionDecl( + SemIR::InstId result_id = ImportCppFunctionDecl( context, loc_id, best_viable_fn->Function, // If this is an operator method, the first arg will be used as self. arg_exprs.size() - (IsOperatorMethodDecl(best_viable_fn->Function) ? 1 : 0)); - return result; + CheckOverloadAccess(context, loc_id, overload_set, + best_viable_fn->FoundDecl, result_id); + return result_id; } case clang::OverloadingResult::OR_No_Viable_Function: { candidate_set.NoteCandidates( diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index 03ac4619ae53..85e5ac87a3af 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -96,10 +96,8 @@ static auto IsInstanceType(Context& context, SemIR::TypeId type_id) -> bool { return false; } -// Returns the highest allowed access. For example, if this returns `Protected` -// then only `Public` and `Protected` accesses are allowed--not `Private`. -static auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id, - SemIR::ConstantId name_scope_const_id) +auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id, + SemIR::ConstantId name_scope_const_id) -> SemIR::AccessKind { SemIR::ScopeLookupResult lookup_result = LookupUnqualifiedName(context, loc_id, SemIR::NameId::SelfType, diff --git a/toolchain/check/member_access.h b/toolchain/check/member_access.h index cdad9757b453..ddf84ef1f9e6 100644 --- a/toolchain/check/member_access.h +++ b/toolchain/check/member_access.h @@ -10,6 +10,13 @@ namespace Carbon::Check { +// Returns the highest allowed access for members of `name_scope_const_id`. For +// example, if this returns `Protected` then only `Public` and `Protected` +// accesses are allowed -- not `Private`. +auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id, + SemIR::ConstantId name_scope_const_id) + -> SemIR::AccessKind; + // Creates SemIR to perform a member access with base expression `base_id` and // member name `name_id`. When `required`, failing to find the name is a // diagnosed error; otherwise, `None` is returned. Returns the result of the diff --git a/toolchain/check/name_lookup.cpp b/toolchain/check/name_lookup.cpp index 7095a562e434..171ab64315ba 100644 --- a/toolchain/check/name_lookup.cpp +++ b/toolchain/check/name_lookup.cpp @@ -195,7 +195,7 @@ auto LookupNameInExactScope(Context& context, SemIR::LocId loc_id, // Prints diagnostics on invalid qualified name access. static auto DiagnoseInvalidQualifiedNameAccess( - Context& context, SemIR::LocId loc_id, SemIR::InstId scope_result_id, + Context& context, SemIR::LocId loc_id, SemIR::LocId member_loc_id, SemIR::NameId name_id, SemIR::AccessKind access_kind, bool is_parent_access, AccessInfo access_info) -> void { auto class_type = context.insts().TryGetAs( @@ -231,7 +231,7 @@ static auto DiagnoseInvalidQualifiedNameAccess( context.emitter() .Build(loc_id, ClassInvalidMemberAccess, access_kind == SemIR::AccessKind::Private, name_id, parent_type_id) - .Note(scope_result_id, ClassMemberDeclaration) + .Note(member_loc_id, ClassMemberDeclaration) .Emit(); } @@ -255,6 +255,17 @@ static auto IsAccessProhibited(std::optional access_info, } } +auto CheckAccess(Context& context, SemIR::LocId loc_id, + SemIR::LocId member_loc_id, SemIR::NameId name_id, + SemIR::AccessKind access_kind, bool is_parent_access, + AccessInfo access_info) -> void { + if (IsAccessProhibited(access_info, access_kind, is_parent_access)) { + DiagnoseInvalidQualifiedNameAccess(context, loc_id, member_loc_id, name_id, + access_kind, is_parent_access, + access_info); + } +} + // Information regarding a prohibited access. struct ProhibitedAccessInfo { // The resulting inst of the lookup. @@ -475,9 +486,9 @@ auto LookupQualifiedName(Context& context, SemIR::LocId loc_id, // Note, `access_info` is guaranteed to have a value here, since // `prohibited_accesses` is non-empty. - DiagnoseInvalidQualifiedNameAccess(context, loc_id, scope_result_id, - name_id, access_kind, - is_parent_access, *access_info); + DiagnoseInvalidQualifiedNameAccess( + context, loc_id, SemIR::LocId(scope_result_id), name_id, + access_kind, is_parent_access, *access_info); } } diff --git a/toolchain/check/name_lookup.h b/toolchain/check/name_lookup.h index c58fdbca77cb..6380360a8a9b 100644 --- a/toolchain/check/name_lookup.h +++ b/toolchain/check/name_lookup.h @@ -99,6 +99,13 @@ auto LookupQualifiedName(Context& context, SemIR::LocId loc_id, auto LookupNameInCore(Context& context, SemIR::LocId loc_id, llvm::StringRef name) -> SemIR::InstId; +// Checks whether a name is accessible in the given access context. Produces a +// diagnostic if not. +auto CheckAccess(Context& context, SemIR::LocId loc_id, + SemIR::LocId member_loc_id, SemIR::NameId name_id, + SemIR::AccessKind access_kind, bool is_parent_access, + AccessInfo access_info) -> void; + // Prints a diagnostic for a duplicate name. auto DiagnoseDuplicateName(Context& context, SemIR::NameId name_id, SemIR::LocId dup_def, SemIR::LocId prev_def) -> void; diff --git a/toolchain/check/testdata/interop/cpp/class/access.carbon b/toolchain/check/testdata/interop/cpp/class/access.carbon index b7727162533a..aab808ba984b 100644 --- a/toolchain/check/testdata/interop/cpp/class/access.carbon +++ b/toolchain/check/testdata/interop/cpp/class/access.carbon @@ -10,8 +10,6 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/class/access.carbon -// TODO: Tests marked as `fail_todo_5891_` to fixed as a follow-up of https://github.com/carbon-language/carbon-lang/pull/5891. - // ============================================================================ // Public non-function member // ============================================================================ @@ -233,14 +231,17 @@ fn F(c: Cpp.C*) { class C { public: + C(); static auto foo() -> void; protected: + C(int); static auto foo(int a) -> void; private: + C(int, int); static auto foo(int a, int b) -> void; }; -// --- fail_todo_5891_import_overload_set_public.carbon +// --- import_overload_set_public.carbon library "[[@TEST_NAME]]"; @@ -248,51 +249,109 @@ import Cpp library "overload_set.h"; fn F() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_5891_import_overload_set_public.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Overloaded set with mixed access` [SemanticsTodo] - // CHECK:STDERR: Cpp.C.foo(); - // CHECK:STDERR: ^~~~~~~~~ - // CHECK:STDERR: fail_todo_5891_import_overload_set_public.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: Cpp.C.foo(); - // CHECK:STDERR: ^~~~~~~~~ - // CHECK:STDERR: + Cpp.C.C(); Cpp.C.foo(); //@dump-sem-ir-end } -// --- fail_todo_5891_import_overload_set_protected.carbon +// --- fail_import_overload_set_protected.carbon library "[[@TEST_NAME]]"; import Cpp library "overload_set.h"; fn F() { - // CHECK:STDERR: fail_todo_5891_import_overload_set_protected.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Overloaded set with mixed access` [SemanticsTodo] + // CHECK:STDERR: fail_import_overload_set_protected.carbon:[[@LINE+5]]:3: error: cannot access protected member `C` of type `Cpp.C` [ClassInvalidMemberAccess] + // CHECK:STDERR: Cpp.C.C(1 as i32); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_overload_set_protected.carbon: note: declared here [ClassMemberDeclaration] + // CHECK:STDERR: + Cpp.C.C(1 as i32); + + // CHECK:STDERR: fail_import_overload_set_protected.carbon:[[@LINE+5]]:3: error: cannot access protected member `foo` of type `Cpp.C` [ClassInvalidMemberAccess] // CHECK:STDERR: Cpp.C.foo(1 as i32); - // CHECK:STDERR: ^~~~~~~~~ - // CHECK:STDERR: fail_todo_5891_import_overload_set_protected.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: Cpp.C.foo(1 as i32); - // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_overload_set_protected.carbon: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: Cpp.C.foo(1 as i32); } -// --- fail_todo_5891_import_overload_set_private.carbon +// --- import_overload_set_protected_base.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "overload_set.h"; + +class D { + extend base: Cpp.C; + + fn MakePublic() -> D { + return {.base = C()}; + } + + fn MakeProtected() -> D { + return {.base = C(1 as i32)}; + } + + fn CallPublic() { + foo(); + } + + fn CallProtected() { + foo(1 as i32); + } +} + +// --- fail_import_overload_set_private.carbon library "[[@TEST_NAME]]"; import Cpp library "overload_set.h"; fn F() { - // CHECK:STDERR: fail_todo_5891_import_overload_set_private.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Overloaded set with mixed access` [SemanticsTodo] + // CHECK:STDERR: fail_import_overload_set_private.carbon:[[@LINE+5]]:3: error: cannot access private member `C` of type `Cpp.C` [ClassInvalidMemberAccess] + // CHECK:STDERR: Cpp.C.C(1 as i32, 2 as i32); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_overload_set_private.carbon: note: declared here [ClassMemberDeclaration] + // CHECK:STDERR: + Cpp.C.C(1 as i32, 2 as i32); + + // CHECK:STDERR: fail_import_overload_set_private.carbon:[[@LINE+5]]:3: error: cannot access private member `foo` of type `Cpp.C` [ClassInvalidMemberAccess] // CHECK:STDERR: Cpp.C.foo(1 as i32, 2 as i32); - // CHECK:STDERR: ^~~~~~~~~ - // CHECK:STDERR: fail_todo_5891_import_overload_set_private.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: Cpp.C.foo(1 as i32, 2 as i32); - // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_overload_set_private.carbon: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: Cpp.C.foo(1 as i32, 2 as i32); } +// --- fail_import_overload_set_private_base.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "overload_set.h"; + +class D { + extend base: Cpp.C; + + fn MakePrivate() -> D { + // CHECK:STDERR: fail_import_overload_set_private_base.carbon:[[@LINE+5]]:21: error: cannot access private member `C` of type `Cpp.C` [ClassInvalidMemberAccess] + // CHECK:STDERR: return {.base = C(1 as i32, 2 as i32)}; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_overload_set_private_base.carbon: note: declared here [ClassMemberDeclaration] + // CHECK:STDERR: + return {.base = C(1 as i32, 2 as i32)}; + } + + fn CallPrivate() { + // CHECK:STDERR: fail_import_overload_set_private_base.carbon:[[@LINE+5]]:5: error: cannot access private member `foo` of type `Cpp.C` [ClassInvalidMemberAccess] + // CHECK:STDERR: foo(1 as i32, 2 as i32); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_overload_set_private_base.carbon: note: declared here [ClassMemberDeclaration] + // CHECK:STDERR: + foo(1 as i32, 2 as i32); + } +} + // ============================================================================ // Base class // ============================================================================ @@ -459,10 +518,24 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_5891_import_overload_set_public.carbon +// CHECK:STDOUT: --- import_overload_set_public.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: %.d40: type = cpp_overload_set_type @C__carbon_thunk [concrete] +// CHECK:STDOUT: %empty_struct.e73: %.d40 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.d9e: 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: %.cd1: type = cpp_overload_set_type @C.foo [concrete] +// CHECK:STDOUT: %empty_struct.b85: %.cd1 = struct_value () [concrete] +// CHECK:STDOUT: %C.foo.type: type = fn_type @C.foo [concrete] +// CHECK:STDOUT: %C.foo: %C.foo.type = 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: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -471,13 +544,37 @@ fn F() { // 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__carbon_thunk [concrete = constants.%empty_struct.e73] +// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %.e4a: %.cd1 = cpp_overload_set_value @C.foo [concrete = constants.%empty_struct.b85] +// CHECK:STDOUT: %C.foo.decl: %C.foo.type = fn_decl @C.foo [concrete = constants.%C.foo] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C] -// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: %Cpp.ref.loc8: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc8_6: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc8_8: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct.e73] +// CHECK:STDOUT: %.loc8_11.1: ref %C = temporary_storage +// CHECK:STDOUT: %addr.loc8_11.1: %ptr.d9e = addr_of %.loc8_11.1 +// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_11.1) +// CHECK:STDOUT: %.loc8_11.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_11.1 +// CHECK:STDOUT: %.loc8_11.3: ref %C = temporary %.loc8_11.1, %.loc8_11.2 +// CHECK:STDOUT: %Cpp.ref.loc9: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %C.ref.loc9: type = name_ref C, imports.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %foo.ref: %.cd1 = name_ref foo, imports.%.e4a [concrete = constants.%empty_struct.b85] +// CHECK:STDOUT: %C.foo.call: init %empty_tuple.type = call imports.%C.foo.decl() +// CHECK:STDOUT: %facet_value: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value] +// CHECK:STDOUT: %.loc8_11.4: %type_where = converted constants.%C, %facet_value [concrete = constants.%facet_value] +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound: = bound_method %.loc8_11.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %.loc8_11.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %addr.loc8_11.2: %ptr.d9e = addr_of %.loc8_11.3 +// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc8_11.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/function/overloads.carbon b/toolchain/check/testdata/interop/cpp/function/overloads.carbon index af546f357554..5a445f71ee7d 100644 --- a/toolchain/check/testdata/interop/cpp/function/overloads.carbon +++ b/toolchain/check/testdata/interop/cpp/function/overloads.carbon @@ -238,54 +238,6 @@ fn F() { Cpp.foo(1 as i32); } -// ============================================================================ -// Overloads access -// ============================================================================ - -// --- same_access_level.h - -struct S { - public: - static auto foo(short a) -> void; - static auto foo(int a) -> void; -}; - -// --- import_same_access_level.carbon - -library "[[@TEST_NAME]]"; - -import Cpp library "same_access_level.h"; - -fn F() { - Cpp.S.foo(1 as i32); -} - -// --- mixed_access_level.h - -struct S { - public: - static auto foo(int a) -> void; - protected: - static auto foo(short a) -> void; -}; - -// --- fail_todo_import_mixed_access_level.carbon - -library "[[@TEST_NAME]]"; - -import Cpp library "mixed_access_level.h"; - -fn F() { - // CHECK:STDERR: fail_todo_import_mixed_access_level.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Overloaded set with mixed access` [SemanticsTodo] - // CHECK:STDERR: Cpp.S.foo(1 as i32); - // CHECK:STDERR: ^~~~~~~~~ - // CHECK:STDERR: fail_todo_import_mixed_access_level.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] - // CHECK:STDERR: Cpp.S.foo(1 as i32); - // CHECK:STDERR: ^~~~~~~~~ - // CHECK:STDERR: - Cpp.S.foo(1 as i32); -} - // ============================================================================ // No viable function found // ============================================================================ @@ -1671,207 +1623,6 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: fn @foo__carbon_thunk(%a.param: %ptr); // CHECK:STDOUT: -// CHECK:STDOUT: --- import_same_access_level.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] -// CHECK:STDOUT: %S: type = class_type @S [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %.177: type = cpp_overload_set_type @As.Convert [concrete] -// CHECK:STDOUT: %empty_struct: %.177 = struct_value () [concrete] -// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] -// CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] -// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] -// CHECK:STDOUT: %As.type.90f: type = generic_interface_type @As [concrete] -// CHECK:STDOUT: %As.generic: %As.type.90f = struct_value () [concrete] -// CHECK:STDOUT: %As.type.dbd: type = facet_type <@As, @As(%i32)> [concrete] -// CHECK:STDOUT: %As.Convert.type.99b: type = fn_type @As.Convert, @As(%i32) [concrete] -// CHECK:STDOUT: %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.565: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.d2c: %Core.IntLiteral.as.As.impl.Convert.type.565 = struct_value () [symbolic] -// CHECK:STDOUT: %As.impl_witness.080: = impl_witness imports.%As.impl_witness_table.5ad, @Core.IntLiteral.as.As.impl(%int_32) [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.aaf: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.414: %Core.IntLiteral.as.As.impl.Convert.type.aaf = struct_value () [concrete] -// CHECK:STDOUT: %As.facet: %As.type.dbd = facet_value Core.IntLiteral, (%As.impl_witness.080) [concrete] -// CHECK:STDOUT: %.351: type = fn_type_with_self_type %As.Convert.type.99b, %As.facet [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.414 [concrete] -// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.As.impl.Convert.414, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete] -// CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] -// CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] -// CHECK:STDOUT: %S.foo.type: type = fn_type @S.foo [concrete] -// CHECK:STDOUT: %S.foo: %S.foo.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: .Int = %Core.Int -// CHECK:STDOUT: .As = %Core.As -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } -// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { -// CHECK:STDOUT: .S = %S.decl -// CHECK:STDOUT: import Cpp//... -// CHECK:STDOUT: } -// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {} -// CHECK:STDOUT: %.dcb: %.177 = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct] -// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] -// CHECK:STDOUT: %Core.As: %As.type.90f = import_ref Core//prelude/parts/as, As, loaded [concrete = constants.%As.generic] -// CHECK:STDOUT: %Core.import_ref.99c: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.565) = import_ref Core//prelude/parts/int, loc32_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.d2c)] -// CHECK:STDOUT: %As.impl_witness_table.5ad = impl_witness_table (%Core.import_ref.99c), @Core.IntLiteral.as.As.impl [concrete] -// CHECK:STDOUT: %S.foo.decl: %S.foo.type = fn_decl @S.foo [concrete = constants.%S.foo] { -// CHECK:STDOUT: %a.patt: %pattern_type.7ce = binding_pattern a [concrete] -// CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt, call_param0 [concrete] -// CHECK:STDOUT: } { -// CHECK:STDOUT: %a.param: %i32 = value_param call_param0 -// CHECK:STDOUT: %.1: type = splice_block %i32 [concrete = constants.%i32] { -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: } -// CHECK:STDOUT: %a: %i32 = bind_name a, %a.param -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .Cpp = imports.%Cpp -// CHECK:STDOUT: .F = %F.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { -// CHECK:STDOUT: import Cpp "same_access_level.h" -// CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @S { -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .foo = imports.%.dcb -// CHECK:STDOUT: import Cpp//... -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S] -// CHECK:STDOUT: %foo.ref: %.177 = name_ref foo, imports.%.dcb [concrete = constants.%empty_struct] -// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: %impl.elem0: %.351 = impl_witness_access constants.%As.impl_witness.080, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.414] -// CHECK:STDOUT: %bound_method.loc7_15.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] -// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn] -// CHECK:STDOUT: %bound_method.loc7_15.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc7_15.2(%int_1) [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %.loc7_15.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %.loc7_15.2: %i32 = converted %int_1, %.loc7_15.1 [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %S.foo.call: init %empty_tuple.type = call imports.%S.foo.decl(%.loc7_15.2) -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @S.foo(%a.param: %i32); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_mixed_access_level.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] -// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] -// CHECK:STDOUT: %S: type = class_type @S [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] -// CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] -// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] -// CHECK:STDOUT: %As.type.90f: type = generic_interface_type @As [concrete] -// CHECK:STDOUT: %As.generic: %As.type.90f = struct_value () [concrete] -// CHECK:STDOUT: %As.type.dbd: type = facet_type <@As, @As(%i32)> [concrete] -// CHECK:STDOUT: %As.Convert.type.99b: type = fn_type @As.Convert, @As(%i32) [concrete] -// CHECK:STDOUT: %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.565: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.d2c: %Core.IntLiteral.as.As.impl.Convert.type.565 = struct_value () [symbolic] -// CHECK:STDOUT: %As.impl_witness.080: = impl_witness imports.%As.impl_witness_table.5ad, @Core.IntLiteral.as.As.impl(%int_32) [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.aaf: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.414: %Core.IntLiteral.as.As.impl.Convert.type.aaf = struct_value () [concrete] -// CHECK:STDOUT: %As.facet: %As.type.dbd = facet_value Core.IntLiteral, (%As.impl_witness.080) [concrete] -// CHECK:STDOUT: %.351: type = fn_type_with_self_type %As.Convert.type.99b, %As.facet [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.414 [concrete] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.As.impl.Convert.414, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete] -// CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] -// CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: .Int = %Core.Int -// CHECK:STDOUT: .As = %Core.As -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } -// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { -// CHECK:STDOUT: .S = %S.decl -// CHECK:STDOUT: import Cpp//... -// CHECK:STDOUT: } -// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {} -// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] -// CHECK:STDOUT: %Core.As: %As.type.90f = import_ref Core//prelude/parts/as, As, loaded [concrete = constants.%As.generic] -// CHECK:STDOUT: %Core.import_ref.99c: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.565) = import_ref Core//prelude/parts/int, loc32_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.d2c)] -// CHECK:STDOUT: %As.impl_witness_table.5ad = impl_witness_table (%Core.import_ref.99c), @Core.IntLiteral.as.As.impl [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .Cpp = imports.%Cpp -// CHECK:STDOUT: .F = %F.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { -// CHECK:STDOUT: import Cpp "mixed_access_level.h" -// CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @S { -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .foo = -// CHECK:STDOUT: import Cpp//... -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S] -// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] -// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: %impl.elem0: %.351 = impl_witness_access constants.%As.impl_witness.080, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.414] -// CHECK:STDOUT: %bound_method.loc14_15.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] -// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn] -// CHECK:STDOUT: %bound_method.loc14_15.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] -// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc14_15.2(%int_1) [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %.loc14_15.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: %.loc14_15.2: %i32 = converted %int_1, %.loc14_15.1 [concrete = constants.%int_1.5d2] -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_no_viable_function.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/sem_ir/cpp_overload_set.h b/toolchain/sem_ir/cpp_overload_set.h index 229dfc07ad85..3b987662e693 100644 --- a/toolchain/sem_ir/cpp_overload_set.h +++ b/toolchain/sem_ir/cpp_overload_set.h @@ -21,6 +21,9 @@ struct CppOverloadSet : public Printable { // The parent scope. NameScopeId parent_scope_id; + // The naming class in the name lookup that found this overload set. + clang::CXXRecordDecl* naming_class; + // List of all named decls found at name lookup. // TODO: Find a good small size for the UnresolvedSet or rework how we // store the candidates.