From f0afe9307f6768a9bbc68b84ed2a39d162a0d1e9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 14 Oct 2014 02:00:47 +0000 Subject: [PATCH] [modules] Merging for class-scope using-declarations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@219657 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 29 +++++++- lib/Sema/SemaTemplateInstantiateDecl.cpp | 13 ++-- lib/Serialization/ASTReaderDecl.cpp | 68 ++++++++++++++++++ test/Modules/Inputs/merge-using-decls/a.h | 43 ++++++++++++ test/Modules/Inputs/merge-using-decls/b.h | 50 ++++++++++++++ .../Inputs/merge-using-decls/module.modulemap | 2 + test/Modules/merge-using-decls.cpp | 69 +++++++++++++++++++ 7 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 test/Modules/Inputs/merge-using-decls/a.h create mode 100644 test/Modules/Inputs/merge-using-decls/b.h create mode 100644 test/Modules/Inputs/merge-using-decls/module.modulemap create mode 100644 test/Modules/merge-using-decls.cpp diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 98efa6c5b6..6d09f49f75 100644 --- a/include/clang/AST/DeclCXX.h +++ b/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 { 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::foo; /// }; /// \endcode -class UnresolvedUsingValueDecl : public ValueDecl { +class UnresolvedUsingValueDecl : public ValueDecl, + public Mergeable { 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 { 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/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index a07f7765fc..274105d532 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/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(Other)) { if (!Field->getDeclName()) { // This is an unnamed field. - return Ctx.getInstantiatedFromUnnamedFieldDecl(Field) == - cast(D); + return declaresSameEntity(Ctx.getInstantiatedFromUnnamedFieldDecl(Field), + cast(D)); } } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 51d930073d..a3b773f6fc 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1224,6 +1224,7 @@ void ASTDeclReader::VisitUsingDecl(UsingDecl *D) { D->setTypename(Record[Idx++]); if (NamedDecl *Pattern = ReadDeclAs(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(X)) { + auto *UY = cast(Y); + return isSameQualifier(UX->getQualifier(), UY->getQualifier()) && + UX->hasTypename() == UY->hasTypename() && + UX->isAccessDeclaration() == UY->isAccessDeclaration(); + } + if (auto *UX = dyn_cast(X)) { + auto *UY = cast(Y); + return isSameQualifier(UX->getQualifier(), UY->getQualifier()) && + UX->isAccessDeclaration() == UY->isAccessDeclaration(); + } + if (auto *UX = dyn_cast(X)) + return isSameQualifier( + UX->getQualifier(), + cast(Y)->getQualifier()); + // Namespace alias definitions with the same target match. if (auto *NAX = dyn_cast(X)) { auto *NAY = cast(Y); diff --git a/test/Modules/Inputs/merge-using-decls/a.h b/test/Modules/Inputs/merge-using-decls/a.h new file mode 100644 index 0000000000..0fe0067bf2 --- /dev/null +++ b/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 struct C : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template struct D : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template struct E : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template struct F : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +// Force instantiation. +typedef C::type I; +typedef D::type I; +typedef E::type I; +typedef F::type I; diff --git a/test/Modules/Inputs/merge-using-decls/b.h b/test/Modules/Inputs/merge-using-decls/b.h new file mode 100644 index 0000000000..359555570a --- /dev/null +++ b/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 struct C : X, T { + using T::value; + using typename T::type; + using X::v; + using typename X::t; +}; + +template 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 struct E : X, T { + // Mismatch in using/access-declaration-ness. + T::value; + X::v; +}; + +template 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::type I; +typedef D::t I; +typedef E::type I; +typedef F::type I; diff --git a/test/Modules/Inputs/merge-using-decls/module.modulemap b/test/Modules/Inputs/merge-using-decls/module.modulemap new file mode 100644 index 0000000000..a415527813 --- /dev/null +++ b/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/test/Modules/merge-using-decls.cpp b/test/Modules/merge-using-decls.cpp new file mode 100644 index 0000000000..3b84d0e5a3 --- /dev/null +++ b/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 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 int UseAll() { + return Use >() + Use >() + Use >() + Use >(); // expected-note 0-2{{instantiation of}} +} + +template int UseAll(); +template int UseAll(); +template int UseAll(); + +#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' in module 'A'}} +// expected-error@b.h:* {{'E::v' from module 'B' is not present in definition of 'E' in module 'A'}} + +// expected-error@b.h:* {{'F::type' from module 'B' is not present in definition of 'F' in module 'A'}} +// expected-error@b.h:* {{'F::t' from module 'B' is not present in definition of 'F' in module 'A'}} +// expected-error@b.h:* {{'F::value' from module 'B' is not present in definition of 'F' in module 'A'}} +// expected-error@b.h:* {{'F::v' from module 'B' is not present in definition of 'F' 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' in module 'B'}} +// expected-error@a.h:* {{'D::value' from module 'A' is not present in definition of 'D' 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' in module 'B'}} +// expected-error@a.h:* {{'E::t' from module 'A' is not present in definition of 'E' in module 'B'}} +// expected-error@a.h:* {{'E::value' from module 'A' is not present in definition of 'E' in module 'B'}} +// expected-error@a.h:* {{'E::v' from module 'A' is not present in definition of 'E' 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' in module 'B'}} +// expected-error@a.h:* {{'F::t' from module 'A' is not present in definition of 'F' in module 'B'}} +// expected-error@a.h:* {{'F::value' from module 'A' is not present in definition of 'F' in module 'B'}} +// expected-error@a.h:* {{'F::v' from module 'A' is not present in definition of 'F' in module 'B'}} + +// expected-note@b.h:* +{{does not match}} +// expected-note@b.h:* +{{target of using}} +#endif -- 2.40.0