diff options
-rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 29 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 13 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderDecl.cpp | 68 | ||||
-rw-r--r-- | clang/test/Modules/Inputs/merge-using-decls/a.h | 43 | ||||
-rw-r--r-- | clang/test/Modules/Inputs/merge-using-decls/b.h | 50 | ||||
-rw-r--r-- | clang/test/Modules/Inputs/merge-using-decls/module.modulemap | 2 | ||||
-rw-r--r-- | clang/test/Modules/merge-using-decls.cpp | 69 |
7 files changed, 265 insertions, 9 deletions
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 98efa6c5b6c..6d09f49f75e 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -2851,7 +2851,7 @@ public: /// \code /// using someNameSpace::someIdentifier; /// \endcode -class UsingDecl : public NamedDecl { +class UsingDecl : public NamedDecl, public Mergeable<UsingDecl> { void anchor() override; /// \brief The source location of the 'using' keyword itself. @@ -2975,6 +2975,10 @@ public: SourceRange getSourceRange() const override LLVM_READONLY; + /// Retrieves the canonical declaration of this declaration. + UsingDecl *getCanonicalDecl() override { return getFirstDecl(); } + const UsingDecl *getCanonicalDecl() const { return getFirstDecl(); } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Using; } @@ -2993,7 +2997,8 @@ public: /// using Base<T>::foo; /// }; /// \endcode -class UnresolvedUsingValueDecl : public ValueDecl { +class UnresolvedUsingValueDecl : public ValueDecl, + public Mergeable<UnresolvedUsingValueDecl> { void anchor() override; /// \brief The source location of the 'using' keyword @@ -3049,6 +3054,14 @@ public: SourceRange getSourceRange() const override LLVM_READONLY; + /// Retrieves the canonical declaration of this declaration. + UnresolvedUsingValueDecl *getCanonicalDecl() override { + return getFirstDecl(); + } + const UnresolvedUsingValueDecl *getCanonicalDecl() const { + return getFirstDecl(); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == UnresolvedUsingValue; } @@ -3067,7 +3080,9 @@ public: /// /// The type associated with an unresolved using typename decl is /// currently always a typename type. -class UnresolvedUsingTypenameDecl : public TypeDecl { +class UnresolvedUsingTypenameDecl + : public TypeDecl, + public Mergeable<UnresolvedUsingTypenameDecl> { void anchor() override; /// \brief The source location of the 'typename' keyword @@ -3111,6 +3126,14 @@ public: static UnresolvedUsingTypenameDecl * CreateDeserialized(ASTContext &C, unsigned ID); + /// Retrieves the canonical declaration of this declaration. + UnresolvedUsingTypenameDecl *getCanonicalDecl() override { + return getFirstDecl(); + } + const UnresolvedUsingTypenameDecl *getCanonicalDecl() const { + return getFirstDecl(); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == UnresolvedUsingTypename; } }; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index a07f7765fcb..274105d532a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4293,25 +4293,26 @@ static bool isInstantiationOf(EnumDecl *Pattern, static bool isInstantiationOf(UsingShadowDecl *Pattern, UsingShadowDecl *Instance, ASTContext &C) { - return C.getInstantiatedFromUsingShadowDecl(Instance) == Pattern; + return declaresSameEntity(C.getInstantiatedFromUsingShadowDecl(Instance), + Pattern); } static bool isInstantiationOf(UsingDecl *Pattern, UsingDecl *Instance, ASTContext &C) { - return C.getInstantiatedFromUsingDecl(Instance) == Pattern; + return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern); } static bool isInstantiationOf(UnresolvedUsingValueDecl *Pattern, UsingDecl *Instance, ASTContext &C) { - return C.getInstantiatedFromUsingDecl(Instance) == Pattern; + return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern); } static bool isInstantiationOf(UnresolvedUsingTypenameDecl *Pattern, UsingDecl *Instance, ASTContext &C) { - return C.getInstantiatedFromUsingDecl(Instance) == Pattern; + return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern); } static bool isInstantiationOfStaticDataMember(VarDecl *Pattern, @@ -4377,8 +4378,8 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) { if (FieldDecl *Field = dyn_cast<FieldDecl>(Other)) { if (!Field->getDeclName()) { // This is an unnamed field. - return Ctx.getInstantiatedFromUnnamedFieldDecl(Field) == - cast<FieldDecl>(D); + return declaresSameEntity(Ctx.getInstantiatedFromUnnamedFieldDecl(Field), + cast<FieldDecl>(D)); } } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 51d930073dc..a3b773f6fc5 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1224,6 +1224,7 @@ void ASTDeclReader::VisitUsingDecl(UsingDecl *D) { D->setTypename(Record[Idx++]); if (NamedDecl *Pattern = ReadDeclAs<NamedDecl>(Record, Idx)) Reader.getContext().setInstantiatedFromUsingDecl(D, Pattern); + mergeMergeable(D); } void ASTDeclReader::VisitUsingShadowDecl(UsingShadowDecl *D) { @@ -1251,6 +1252,7 @@ void ASTDeclReader::VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D) { D->setUsingLoc(ReadSourceLocation(Record, Idx)); D->QualifierLoc = Reader.ReadNestedNameSpecifierLoc(F, Record, Idx); ReadDeclarationNameLoc(D->DNLoc, D->getDeclName(), Record, Idx); + mergeMergeable(D); } void ASTDeclReader::VisitUnresolvedUsingTypenameDecl( @@ -1258,6 +1260,7 @@ void ASTDeclReader::VisitUnresolvedUsingTypenameDecl( VisitTypeDecl(D); D->TypenameLocation = ReadSourceLocation(Record, Idx); D->QualifierLoc = Reader.ReadNestedNameSpecifierLoc(F, Record, Idx); + mergeMergeable(D); } void ASTDeclReader::ReadCXXDefinitionData( @@ -2341,6 +2344,53 @@ static bool isSameTemplateParameter(const NamedDecl *X, TY->getTemplateParameters()); } +static NamespaceDecl *getNamespace(const NestedNameSpecifier *X) { + if (auto *NS = X->getAsNamespace()) + return NS; + if (auto *NAS = X->getAsNamespaceAlias()) + return NAS->getNamespace(); + return nullptr; +} + +static bool isSameQualifier(const NestedNameSpecifier *X, + const NestedNameSpecifier *Y) { + if (auto *NSX = getNamespace(X)) { + auto *NSY = getNamespace(Y); + if (!NSY || NSX->getCanonicalDecl() != NSY->getCanonicalDecl()) + return false; + } else if (X->getKind() != Y->getKind()) + return false; + + // FIXME: For namespaces and types, we're permitted to check that the entity + // is named via the same tokens. We should probably do so. + switch (X->getKind()) { + case NestedNameSpecifier::Identifier: + if (X->getAsIdentifier() != Y->getAsIdentifier()) + return false; + break; + case NestedNameSpecifier::Namespace: + case NestedNameSpecifier::NamespaceAlias: + // We've already checked that we named the same namespace. + break; + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + if (X->getAsType()->getCanonicalTypeInternal() != + Y->getAsType()->getCanonicalTypeInternal()) + return false; + break; + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Super: + return true; + } + + // Recurse into earlier portion of NNS, if any. + auto *PX = X->getPrefix(); + auto *PY = Y->getPrefix(); + if (PX && PY) + return isSameQualifier(PX, PY); + return !PX && !PY; +} + /// \brief Determine whether two template parameter lists are similar enough /// that they may be used in declarations of the same template. static bool isSameTemplateParameterList(const TemplateParameterList *X, @@ -2448,6 +2498,24 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { return USX->getTargetDecl() == USY->getTargetDecl(); } + // Using declarations with the same qualifier match. (We already know that + // the name matches.) + if (auto *UX = dyn_cast<UsingDecl>(X)) { + auto *UY = cast<UsingDecl>(Y); + return isSameQualifier(UX->getQualifier(), UY->getQualifier()) && + UX->hasTypename() == UY->hasTypename() && + UX->isAccessDeclaration() == UY->isAccessDeclaration(); + } + if (auto *UX = dyn_cast<UnresolvedUsingValueDecl>(X)) { + auto *UY = cast<UnresolvedUsingValueDecl>(Y); + return isSameQualifier(UX->getQualifier(), UY->getQualifier()) && + UX->isAccessDeclaration() == UY->isAccessDeclaration(); + } + if (auto *UX = dyn_cast<UnresolvedUsingTypenameDecl>(X)) + return isSameQualifier( + UX->getQualifier(), + cast<UnresolvedUsingTypenameDecl>(Y)->getQualifier()); + // Namespace alias definitions with the same target match. if (auto *NAX = dyn_cast<NamespaceAliasDecl>(X)) { auto *NAY = cast<NamespaceAliasDecl>(Y); diff --git a/clang/test/Modules/Inputs/merge-using-decls/a.h b/clang/test/Modules/Inputs/merge-using-decls/a.h new file mode 100644 index 00000000000..0fe0067bf23 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-using-decls/a.h @@ -0,0 +1,43 @@ +struct X { + int v; + typedef int t; +}; + +struct YA { + int value; + typedef int type; +}; + +template<typename T> struct C : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template<typename T> struct D : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template<typename T> struct E : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template<typename T> struct F : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +// Force instantiation. +typedef C<YA>::type I; +typedef D<YA>::type I; +typedef E<YA>::type I; +typedef F<YA>::type I; diff --git a/clang/test/Modules/Inputs/merge-using-decls/b.h b/clang/test/Modules/Inputs/merge-using-decls/b.h new file mode 100644 index 00000000000..359555570a4 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-using-decls/b.h @@ -0,0 +1,50 @@ +struct X { + int v; + typedef int t; +}; + +struct YB { + typedef YB Y; + int value; + typedef int type; +}; + +struct YBRev { + typedef int value; + int type; +}; + +template<typename T> struct C : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template<typename T> struct D : X, T { + // Mismatch in type/non-type-ness. + using typename T::value; + using T::type; + using X::v; + using typename X::t; +}; + +template<typename T> struct E : X, T { + // Mismatch in using/access-declaration-ness. + T::value; + X::v; +}; + +template<typename T> struct F : X, T { + // Mismatch in nested-name-specifier. + using T::Y::value; + using typename T::Y::type; + using ::X::v; + using typename ::X::t; +}; + +// Force instantiation. +typedef C<YB>::type I; +typedef D<YBRev>::t I; +typedef E<YB>::type I; +typedef F<YB>::type I; diff --git a/clang/test/Modules/Inputs/merge-using-decls/module.modulemap b/clang/test/Modules/Inputs/merge-using-decls/module.modulemap new file mode 100644 index 00000000000..a415527813c --- /dev/null +++ b/clang/test/Modules/Inputs/merge-using-decls/module.modulemap @@ -0,0 +1,2 @@ +module A { header "a.h" } +module B { header "b.h" } diff --git a/clang/test/Modules/merge-using-decls.cpp b/clang/test/Modules/merge-using-decls.cpp new file mode 100644 index 00000000000..3b84d0e5a3a --- /dev/null +++ b/clang/test/Modules/merge-using-decls.cpp @@ -0,0 +1,69 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -I%S/Inputs/merge-using-decls -verify %s -DORDER=1 +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -I%S/Inputs/merge-using-decls -verify %s -DORDER=2 + +#if ORDER == 1 +#include "a.h" +#include "b.h" +#else +#include "b.h" +#include "a.h" +#endif + +struct Y { + int value; // expected-note 0-1{{target of using}} + typedef int type; // expected-note 0-1{{target of using}} +}; + +template<typename T> int Use() { + int k = T().v + T().value; // expected-note 0-2{{instantiation of}} + typedef typename T::type I; + typedef typename T::t I; + typedef int I; + return k; +} + +template<typename T> int UseAll() { + return Use<C<T> >() + Use<D<T> >() + Use<E<T> >() + Use<F<T> >(); // expected-note 0-2{{instantiation of}} +} + +template int UseAll<YA>(); +template int UseAll<YB>(); +template int UseAll<Y>(); + +#if ORDER == 1 +// Here, we're instantiating the definition from 'A' and merging the definition +// from 'B' into it. + +// expected-error@b.h:* {{'E::value' from module 'B' is not present in definition of 'E<T>' in module 'A'}} +// expected-error@b.h:* {{'E::v' from module 'B' is not present in definition of 'E<T>' in module 'A'}} + +// expected-error@b.h:* {{'F::type' from module 'B' is not present in definition of 'F<T>' in module 'A'}} +// expected-error@b.h:* {{'F::t' from module 'B' is not present in definition of 'F<T>' in module 'A'}} +// expected-error@b.h:* {{'F::value' from module 'B' is not present in definition of 'F<T>' in module 'A'}} +// expected-error@b.h:* {{'F::v' from module 'B' is not present in definition of 'F<T>' in module 'A'}} + +// expected-note@a.h:* +{{does not match}} +#else +// Here, we're instantiating the definition from 'B' and merging the definition +// from 'A' into it. + +// expected-error@a.h:* {{'D::type' from module 'A' is not present in definition of 'D<T>' in module 'B'}} +// expected-error@a.h:* {{'D::value' from module 'A' is not present in definition of 'D<T>' in module 'B'}} +// expected-error@b.h:* 2{{'typename' keyword used on a non-type}} +// expected-error@b.h:* 2{{dependent using declaration resolved to type without 'typename'}} + +// expected-error@a.h:* {{'E::type' from module 'A' is not present in definition of 'E<T>' in module 'B'}} +// expected-error@a.h:* {{'E::t' from module 'A' is not present in definition of 'E<T>' in module 'B'}} +// expected-error@a.h:* {{'E::value' from module 'A' is not present in definition of 'E<T>' in module 'B'}} +// expected-error@a.h:* {{'E::v' from module 'A' is not present in definition of 'E<T>' in module 'B'}} +// expected-note@b.h:* 2{{definition has no member}} + +// expected-error@a.h:* {{'F::type' from module 'A' is not present in definition of 'F<T>' in module 'B'}} +// expected-error@a.h:* {{'F::t' from module 'A' is not present in definition of 'F<T>' in module 'B'}} +// expected-error@a.h:* {{'F::value' from module 'A' is not present in definition of 'F<T>' in module 'B'}} +// expected-error@a.h:* {{'F::v' from module 'A' is not present in definition of 'F<T>' in module 'B'}} + +// expected-note@b.h:* +{{does not match}} +// expected-note@b.h:* +{{target of using}} +#endif |