// If this method has the same name as any base method, this is likely
// necessary even if it's not an override. e.g. CRTP.
- auto FindHidden = [&](const CXXBaseSpecifier *S, clang::CXXBasePath &P) {
- return CXXRecordDecl::FindOrdinaryMember(S, P, Decl->getDeclName());
- };
- CXXBasePaths UnusedPaths;
- if (Decl->getParent()->lookupInBases(FindHidden, UnusedPaths))
- return SK_Invalid;
+ for (const CXXBaseSpecifier &Base : Decl->getParent()->bases())
+ if (const auto *RD = Base.getType()->getAsCXXRecordDecl())
+ if (RD->hasMemberName(Decl->getDeclName()))
+ return SK_Invalid;
if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod])
return SK_ConstexprMethod;
CXXBasePath &Path,
const CXXRecordDecl *BaseRecord);
- /// Base-class lookup callback that determines whether there exists
- /// a tag with the given name.
- ///
- /// This callback can be used with \c lookupInBases() to find tag members
- /// of the given name within a C++ class hierarchy.
- static bool FindTagMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path, DeclarationName Name);
-
- /// Base-class lookup callback that determines whether there exists
- /// a member with the given name.
- ///
- /// This callback can be used with \c lookupInBases() to find members
- /// of the given name within a C++ class hierarchy.
- static bool FindOrdinaryMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path, DeclarationName Name);
-
- /// Base-class lookup callback that determines whether there exists
- /// a member with the given name.
- ///
- /// This callback can be used with \c lookupInBases() to find members
- /// of the given name within a C++ class hierarchy, including dependent
- /// classes.
- static bool
- FindOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path, DeclarationName Name);
-
- /// Base-class lookup callback that determines whether there exists
- /// an OpenMP declare reduction member with the given name.
- ///
- /// This callback can be used with \c lookupInBases() to find members
- /// of the given name within a C++ class hierarchy.
- static bool FindOMPReductionMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path, DeclarationName Name);
-
- /// Base-class lookup callback that determines whether there exists
- /// an OpenMP declare mapper member with the given name.
- ///
- /// This callback can be used with \c lookupInBases() to find members
- /// of the given name within a C++ class hierarchy.
- static bool FindOMPMapperMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path, DeclarationName Name);
-
- /// Base-class lookup callback that determines whether there exists
- /// a member with the given name that can be used in a nested-name-specifier.
- ///
- /// This callback can be used with \c lookupInBases() to find members of
- /// the given name within a C++ class hierarchy that can occur within
- /// nested-name-specifiers.
- static bool FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path,
- DeclarationName Name);
-
/// Retrieve the final overriders for each virtual member
/// function in the class hierarchy where this class is the
/// most-derived class in the class hierarchy.
/// Get the indirect primary bases for this class.
void getIndirectPrimaryBases(CXXIndirectPrimaryBaseSet& Bases) const;
+ /// Determine whether this class has a member with the given name, possibly
+ /// in a non-dependent base class.
+ ///
+ /// No check for ambiguity is performed, so this should never be used when
+ /// implementing language semantics, but it may be appropriate for warnings,
+ /// static analysis, or similar.
+ bool hasMemberName(DeclarationName N) const;
+
/// Performs an imprecise lookup of a dependent name in this class.
///
/// This function does not follow strict semantic rules and should be used
/// only when lookup rules can be relaxed, e.g. indexing.
std::vector<const NamedDecl *>
- lookupDependentName(const DeclarationName &Name,
+ lookupDependentName(DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
/// Renders and displays an inheritance diagram
/// considered valid results.
/// \param AllowDependent Whether unresolved using declarations (that might
/// name templates) should be considered valid results.
- NamedDecl *getAsTemplateNameDecl(NamedDecl *D,
- bool AllowFunctionTemplates = true,
- bool AllowDependent = true);
+ static NamedDecl *getAsTemplateNameDecl(NamedDecl *D,
+ bool AllowFunctionTemplates = true,
+ bool AllowDependent = true);
enum TemplateNameIsRequiredTag { TemplateNameIsRequired };
/// Whether and why a template name is required in this lookup.
->getCanonicalDecl() == BaseRecord;
}
-bool CXXRecordDecl::FindTagMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path,
- DeclarationName Name) {
- RecordDecl *BaseRecord =
- Specifier->getType()->castAs<RecordType>()->getDecl();
-
- for (Path.Decls = BaseRecord->lookup(Name);
- !Path.Decls.empty();
- Path.Decls = Path.Decls.slice(1)) {
- if (Path.Decls.front()->isInIdentifierNamespace(IDNS_Tag))
- return true;
- }
-
- return false;
+static bool isOrdinaryMember(const NamedDecl *ND) {
+ return ND->isInIdentifierNamespace(Decl::IDNS_Ordinary | Decl::IDNS_Tag |
+ Decl::IDNS_Member);
}
-static bool findOrdinaryMember(RecordDecl *BaseRecord, CXXBasePath &Path,
+static bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path,
DeclarationName Name) {
- const unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag |
- Decl::IDNS_Member;
- for (Path.Decls = BaseRecord->lookup(Name);
- !Path.Decls.empty();
- Path.Decls = Path.Decls.slice(1)) {
- if (Path.Decls.front()->isInIdentifierNamespace(IDNS))
+ Path.Decls = RD->lookup(Name);
+ for (NamedDecl *ND : Path.Decls)
+ if (isOrdinaryMember(ND))
return true;
- }
return false;
}
-bool CXXRecordDecl::FindOrdinaryMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path,
- DeclarationName Name) {
- RecordDecl *BaseRecord =
- Specifier->getType()->castAs<RecordType>()->getDecl();
- return findOrdinaryMember(BaseRecord, Path, Name);
+bool CXXRecordDecl::hasMemberName(DeclarationName Name) const {
+ CXXBasePath P;
+ if (findOrdinaryMember(this, P, Name))
+ return true;
+
+ CXXBasePaths Paths(false, false, false);
+ return lookupInBases(
+ [Name](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
+ return findOrdinaryMember(Specifier->getType()->getAsCXXRecordDecl(),
+ Path, Name);
+ },
+ Paths);
}
-bool CXXRecordDecl::FindOrdinaryMemberInDependentClasses(
- const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
- DeclarationName Name) {
+static bool
+findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
+ CXXBasePath &Path, DeclarationName Name) {
const TemplateSpecializationType *TST =
Specifier->getType()->getAs<TemplateSpecializationType>();
if (!TST) {
auto *RT = Specifier->getType()->getAs<RecordType>();
if (!RT)
return false;
- return findOrdinaryMember(RT->getDecl(), Path, Name);
+ return findOrdinaryMember(cast<CXXRecordDecl>(RT->getDecl()), Path, Name);
}
TemplateName TN = TST->getTemplateName();
const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
return findOrdinaryMember(RD, Path, Name);
}
-bool CXXRecordDecl::FindOMPReductionMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path,
- DeclarationName Name) {
- RecordDecl *BaseRecord =
- Specifier->getType()->castAs<RecordType>()->getDecl();
-
- for (Path.Decls = BaseRecord->lookup(Name); !Path.Decls.empty();
- Path.Decls = Path.Decls.slice(1)) {
- if (Path.Decls.front()->isInIdentifierNamespace(IDNS_OMPReduction))
- return true;
- }
-
- return false;
-}
-
-bool CXXRecordDecl::FindOMPMapperMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path,
- DeclarationName Name) {
- RecordDecl *BaseRecord =
- Specifier->getType()->castAs<RecordType>()->getDecl();
-
- for (Path.Decls = BaseRecord->lookup(Name); !Path.Decls.empty();
- Path.Decls = Path.Decls.slice(1)) {
- if (Path.Decls.front()->isInIdentifierNamespace(IDNS_OMPMapper))
- return true;
- }
-
- return false;
-}
-
-bool CXXRecordDecl::
-FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path,
- DeclarationName Name) {
- RecordDecl *BaseRecord =
- Specifier->getType()->castAs<RecordType>()->getDecl();
-
- for (Path.Decls = BaseRecord->lookup(Name);
- !Path.Decls.empty();
- Path.Decls = Path.Decls.slice(1)) {
- // FIXME: Refactor the "is it a nested-name-specifier?" check
- if (isa<TypedefNameDecl>(Path.Decls.front()) ||
- Path.Decls.front()->isInIdentifierNamespace(IDNS_Tag))
- return true;
- }
-
- return false;
-}
-
std::vector<const NamedDecl *> CXXRecordDecl::lookupDependentName(
- const DeclarationName &Name,
+ DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
std::vector<const NamedDecl *> Results;
// Lookup in the class.
- DeclContext::lookup_result DirectResult = lookup(Name);
- if (!DirectResult.empty()) {
- for (const NamedDecl *ND : DirectResult) {
- if (Filter(ND))
- Results.push_back(ND);
- }
- return Results;
+ bool AnyOrdinaryMembers = false;
+ for (const NamedDecl *ND : lookup(Name)) {
+ if (isOrdinaryMember(ND))
+ AnyOrdinaryMembers = true;
+ if (Filter(ND))
+ Results.push_back(ND);
}
+ if (AnyOrdinaryMembers)
+ return Results;
+
// Perform lookup into our base classes.
CXXBasePaths Paths;
Paths.setOrigin(this);
if (!lookupInBases(
[&](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
- return CXXRecordDecl::FindOrdinaryMemberInDependentClasses(
- Specifier, Path, Name);
+ return findOrdinaryMemberInDependentClasses(Specifier, Path, Name);
},
Paths, /*LookupInDependent=*/true))
return Results;
for (const NamedDecl *ND : Paths.front().Decls) {
- if (Filter(ND))
+ if (isOrdinaryMember(ND) && Filter(ND))
Results.push_back(ND);
}
return Results;
return Found;
}
-/// Callback that looks for any member of a class with the given name.
-static bool LookupAnyMember(const CXXBaseSpecifier *Specifier,
- CXXBasePath &Path, DeclarationName Name) {
- RecordDecl *BaseRecord = Specifier->getType()->castAs<RecordType>()->getDecl();
-
- Path.Decls = BaseRecord->lookup(Name);
- return !Path.Decls.empty();
-}
-
-/// Determine whether the given set of member declarations contains only
-/// static members, nested types, and enumerators.
-template<typename InputIterator>
-static bool HasOnlyStaticMembers(InputIterator First, InputIterator Last) {
- Decl *D = (*First)->getUnderlyingDecl();
- if (isa<VarDecl>(D) || isa<TypeDecl>(D) || isa<EnumConstantDecl>(D))
- return true;
-
- if (isa<CXXMethodDecl>(D)) {
- // Determine whether all of the methods are static.
- bool AllMethodsAreStatic = true;
- for(; First != Last; ++First) {
- D = (*First)->getUnderlyingDecl();
-
- if (!isa<CXXMethodDecl>(D)) {
- assert(isa<TagDecl>(D) && "Non-function must be a tag decl");
- break;
- }
-
- if (!cast<CXXMethodDecl>(D)->isStatic()) {
- AllMethodsAreStatic = false;
- break;
- }
- }
-
- if (AllMethodsAreStatic)
- return true;
- }
-
- return false;
-}
-
/// Perform qualified name lookup into a given context.
///
/// Qualified name lookup (C++ [basic.lookup.qual]) is used to find
if (!LookupRec || !LookupRec->getDefinition())
return false;
+ // We're done for lookups that can never succeed for C++ classes.
+ if (R.getLookupKind() == LookupOperatorName ||
+ R.getLookupKind() == LookupNamespaceName ||
+ R.getLookupKind() == LookupObjCProtocolName ||
+ R.getLookupKind() == LookupLabel)
+ return false;
+
// If we're performing qualified name lookup into a dependent class,
// then we are actually looking into a current instantiation. If we have any
// dependent base classes, then we either have to delay lookup until
}
// Perform lookup into our base classes.
- CXXBasePaths Paths;
- Paths.setOrigin(LookupRec);
-
- // Look for this member in our base classes
- bool (*BaseCallback)(const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
- DeclarationName Name) = nullptr;
- switch (R.getLookupKind()) {
- case LookupObjCImplicitSelfParam:
- case LookupOrdinaryName:
- case LookupMemberName:
- case LookupRedeclarationWithLinkage:
- case LookupLocalFriendName:
- case LookupDestructorName:
- BaseCallback = &CXXRecordDecl::FindOrdinaryMember;
- break;
-
- case LookupTagName:
- BaseCallback = &CXXRecordDecl::FindTagMember;
- break;
-
- case LookupAnyName:
- BaseCallback = &LookupAnyMember;
- break;
-
- case LookupOMPReductionName:
- BaseCallback = &CXXRecordDecl::FindOMPReductionMember;
- break;
-
- case LookupOMPMapperName:
- BaseCallback = &CXXRecordDecl::FindOMPMapperMember;
- break;
-
- case LookupUsingDeclName:
- // This lookup is for redeclarations only.
-
- case LookupOperatorName:
- case LookupNamespaceName:
- case LookupObjCProtocolName:
- case LookupLabel:
- // These lookups will never find a member in a C++ class (or base class).
- return false;
-
- case LookupNestedNameSpecifierName:
- BaseCallback = &CXXRecordDecl::FindNestedNameSpecifierMember;
- break;
- }
DeclarationName Name = R.getLookupName();
- if (!LookupRec->lookupInBases(
- [=](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
- return BaseCallback(Specifier, Path, Name);
- },
- Paths))
+ unsigned IDNS = R.getIdentifierNamespace();
+
+ // Look for this member in our base classes.
+ auto BaseCallback = [Name, IDNS](const CXXBaseSpecifier *Specifier,
+ CXXBasePath &Path) -> bool {
+ CXXRecordDecl *BaseRecord = Specifier->getType()->getAsCXXRecordDecl();
+ // Drop leading non-matching lookup results from the declaration list so
+ // we don't need to consider them again below.
+ for (Path.Decls = BaseRecord->lookup(Name); !Path.Decls.empty();
+ Path.Decls = Path.Decls.slice(1)) {
+ if (Path.Decls.front()->isInIdentifierNamespace(IDNS))
+ return true;
+ }
+ return false;
+ };
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(LookupRec);
+ if (!LookupRec->lookupInBases(BaseCallback, Paths))
return false;
R.setNamingClass(LookupRec);
int SubobjectNumber = 0;
AccessSpecifier SubobjectAccess = AS_none;
+ // Check whether the given lookup result contains only static members.
+ auto HasOnlyStaticMembers = [&](DeclContextLookupResult Result) {
+ for (NamedDecl *ND : Result)
+ if (ND->isInIdentifierNamespace(IDNS) && ND->isCXXInstanceMember())
+ return false;
+ return true;
+ };
+
+ bool TemplateNameLookup = R.isTemplateNameLookup();
+
+ // Determine whether two sets of members contain the same members, as
+ // required by C++ [class.member.lookup]p6.
+ auto HasSameDeclarations = [IDNS,
+ TemplateNameLookup](DeclContextLookupResult A,
+ DeclContextLookupResult B) {
+ using Iterator = DeclContextLookupResult::iterator;
+ using Result = const Decl *;
+
+ auto Next = [&](Iterator &It, Iterator End) -> Result {
+ while (It != End) {
+ NamedDecl *ND = *It++;
+ if (!ND->isInIdentifierNamespace(IDNS))
+ continue;
+
+ // C++ [temp.local]p3:
+ // A lookup that finds an injected-class-name (10.2) can result in
+ // an ambiguity in certain cases (for example, if it is found in
+ // more than one base class). If all of the injected-class-names
+ // that are found refer to specializations of the same class
+ // template, and if the name is used as a template-name, the
+ // reference refers to the class template itself and not a
+ // specialization thereof, and is not ambiguous.
+ if (TemplateNameLookup)
+ if (auto *TD = getAsTemplateNameDecl(ND))
+ ND = TD;
+
+ // FIXME: Per C++ [class.member.lookup]p3:
+ // type declarations (including injected-class-names are replaced by the
+ // types they designate
+ // So two different typedef declarations with the same name from two
+ // different base classes declaring the same type do not introduce an
+ // ambiguity.
+
+ return cast<NamedDecl>(ND->getUnderlyingDecl()->getCanonicalDecl());
+ }
+ return nullptr;
+ };
+
+ // We'll often find the declarations are in the same order. Handle this
+ // case (and the special case of only one declaration) efficiently.
+ Iterator AIt = A.begin(), BIt = B.begin(), AEnd = A.end(), BEnd = B.end();
+ while (true) {
+ Result AResult = Next(AIt, AEnd);
+ Result BResult = Next(BIt, BEnd);
+ if (!AResult && !BResult)
+ return true;
+ if (!AResult || !BResult)
+ return false;
+ if (AResult != BResult) {
+ // Found a mismatch; carefully check both lists, accounting for the
+ // possibility of declarations appearing more than once.
+ llvm::SmallDenseMap<Result, bool, 32> AResults;
+ for (; AResult; AResult = Next(AIt, AEnd))
+ AResults.insert({AResult, /*FoundInB*/false});
+ unsigned Found = 0;
+ for (; BResult; BResult = Next(BIt, BEnd)) {
+ auto It = AResults.find(BResult);
+ if (It == AResults.end())
+ return false;
+ if (!It->second) {
+ It->second = true;
+ ++Found;
+ }
+ }
+ return AResults.size() == Found;
+ }
+ }
+ };
+
for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathEnd = Paths.end();
Path != PathEnd; ++Path) {
const CXXBasePathElement &PathElement = Path->back();
continue;
}
- if (SubobjectType
- != Context.getCanonicalType(PathElement.Base->getType())) {
+ if (SubobjectType !=
+ Context.getCanonicalType(PathElement.Base->getType())) {
// We found members of the given name in two subobjects of
// different types. If the declaration sets aren't the same, this
// lookup is ambiguous.
- if (HasOnlyStaticMembers(Path->Decls.begin(), Path->Decls.end())) {
- CXXBasePaths::paths_iterator FirstPath = Paths.begin();
- DeclContext::lookup_iterator FirstD = FirstPath->Decls.begin();
- DeclContext::lookup_iterator CurrentD = Path->Decls.begin();
-
- // Get the decl that we should use for deduplicating this lookup.
- auto GetRepresentativeDecl = [&](NamedDecl *D) -> Decl * {
- // C++ [temp.local]p3:
- // A lookup that finds an injected-class-name (10.2) can result in
- // an ambiguity in certain cases (for example, if it is found in
- // more than one base class). If all of the injected-class-names
- // that are found refer to specializations of the same class
- // template, and if the name is used as a template-name, the
- // reference refers to the class template itself and not a
- // specialization thereof, and is not ambiguous.
- if (R.isTemplateNameLookup())
- if (auto *TD = getAsTemplateNameDecl(D))
- D = TD;
- return D->getUnderlyingDecl()->getCanonicalDecl();
- };
-
- while (FirstD != FirstPath->Decls.end() &&
- CurrentD != Path->Decls.end()) {
- if (GetRepresentativeDecl(*FirstD) !=
- GetRepresentativeDecl(*CurrentD))
- break;
-
- ++FirstD;
- ++CurrentD;
- }
-
- if (FirstD == FirstPath->Decls.end() &&
- CurrentD == Path->Decls.end())
- continue;
- }
+ //
+ // FIXME: The language rule says that this applies irrespective of
+ // whether the sets contain only static members.
+ if (HasOnlyStaticMembers(Path->Decls) &&
+ HasSameDeclarations(Paths.begin()->Decls, Path->Decls))
+ continue;
R.setAmbiguousBaseSubobjectTypes(Paths);
return true;
}
+ // FIXME: This language rule no longer exists. Checking for ambiguous base
+ // subobjects should be done as part of formation of a class member access
+ // expression (when converting the object parameter to the member's type).
if (SubobjectNumber != PathElement.SubobjectNumber) {
// We have a different subobject of the same type.
// A static member, a nested type or an enumerator defined in
// a base class T can unambiguously be found even if an object
// has more than one base class subobject of type T.
- if (HasOnlyStaticMembers(Path->Decls.begin(), Path->Decls.end()))
+ if (HasOnlyStaticMembers(Path->Decls))
continue;
// We have found a nonstatic member name in multiple, distinct
for (auto *D : Paths.front().Decls) {
AccessSpecifier AS = CXXRecordDecl::MergeAccess(SubobjectAccess,
D->getAccess());
- R.addDecl(D, AS);
+ if (NamedDecl *ND = R.getAcceptableDecl(D))
+ R.addDecl(ND, AS);
}
R.resolveKind();
return true;
static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD,
StringRef Name) {
const IdentifierInfo &II = Ctx.Idents.get(Name);
- DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II);
- if (!RD->lookup(DeclName).empty())
- return true;
-
- CXXBasePaths Paths(false, false, false);
- if (RD->lookupInBases(
- [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
- return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName);
- },
- Paths))
- return true;
-
- return false;
+ return RD->hasMemberName(Ctx.DeclarationNames.getIdentifier(&II));
}
/// Returns true if the given C++ class is a container or iterator.
union value {
char *String;
};
+
+namespace UnambiguousStaticMemberTemplate {
+ // A static member template is not ambiguous if found in multiple base class
+ // subobjects.
+ struct A { template<typename T> static void f(T); static void g(); };
+ struct B : A { using A::f; using A::g; };
+ struct C : A { using A::f; using A::g; };
+ struct D : B, C {};
+ void f(D d) { d.f(0); d.g(); }
+}
+
+namespace UnambiguousReorderedMembers {
+ // Static members are not ambiguous if we find them in a different order in
+ // multiple base classes.
+ struct A { static void f(); };
+ struct B { static void f(int); };
+ struct C : A, B { using A::f; using B::f; }; // expected-note {{found}}
+ struct D : B, A { using B::f; using A::f; };
+ struct E : C, D {};
+ void f(E e) { e.f(0); }
+
+ // But a different declaration set in different base classes does result in ambiguity.
+ struct X : B, A { using B::f; using A::f; static void f(int, int); }; // expected-note {{found}}
+ struct Y : C, X {};
+ void g(Y y) { y.f(0); } // expected-error {{found in multiple base classes of different types}}
+}
if (cxx_record_decl->lookupInBases(
[decl_name](const clang::CXXBaseSpecifier *specifier,
clang::CXXBasePath &path) {
- return clang::CXXRecordDecl::FindOrdinaryMember(
- specifier, path, decl_name);
+ path.Decls =
+ specifier->getType()->getAsCXXRecordDecl()->lookup(
+ decl_name);
+ return !path.Decls.empty();
},
paths)) {
clang::CXXBasePaths::const_paths_iterator path,