From c8c799318ef2bc679174eca23d34bcfe4aabe63d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 3 Sep 2014 23:11:22 +0000 Subject: [PATCH] [modules] Make NamespaceAliasDecl redeclarable, as it should be. This fixes merging of namespace aliases across modules and improves source fidelity. Incidentally also fixes PR20816. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@217103 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 55 +++++++++++++------ include/clang/Basic/DiagnosticSemaKinds.td | 4 ++ lib/AST/DeclCXX.cpp | 15 +++++- lib/Sema/SemaDeclCXX.cpp | 63 +++++++++++----------- lib/Serialization/ASTCommon.cpp | 10 ++-- lib/Serialization/ASTReaderDecl.cpp | 21 +++++--- lib/Serialization/ASTWriterDecl.cpp | 1 + test/Modules/Inputs/cxx-decls-imported.h | 3 ++ test/Modules/Inputs/cxx-decls-merged.h | 3 ++ test/Modules/cxx-decls.cpp | 2 + test/SemaCXX/namespace-alias.cpp | 6 +-- 11 files changed, 121 insertions(+), 62 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index fcaccc5519..7ebd0e251c 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -2636,7 +2636,8 @@ public: /// \code /// namespace Foo = Bar; /// \endcode -class NamespaceAliasDecl : public NamedDecl { +class NamespaceAliasDecl : public NamedDecl, + public Redeclarable { void anchor() override; /// \brief The location of the \c namespace keyword. @@ -2654,17 +2655,47 @@ class NamespaceAliasDecl : public NamedDecl { /// a NamespaceAliasDecl. NamedDecl *Namespace; - NamespaceAliasDecl(DeclContext *DC, SourceLocation NamespaceLoc, - SourceLocation AliasLoc, IdentifierInfo *Alias, - NestedNameSpecifierLoc QualifierLoc, + NamespaceAliasDecl(ASTContext &C, DeclContext *DC, + SourceLocation NamespaceLoc, SourceLocation AliasLoc, + IdentifierInfo *Alias, NestedNameSpecifierLoc QualifierLoc, SourceLocation IdentLoc, NamedDecl *Namespace) - : NamedDecl(NamespaceAlias, DC, AliasLoc, Alias), - NamespaceLoc(NamespaceLoc), IdentLoc(IdentLoc), - QualifierLoc(QualifierLoc), Namespace(Namespace) { } + : NamedDecl(NamespaceAlias, DC, AliasLoc, Alias), redeclarable_base(C), + NamespaceLoc(NamespaceLoc), IdentLoc(IdentLoc), + QualifierLoc(QualifierLoc), Namespace(Namespace) {} + + typedef Redeclarable redeclarable_base; + NamespaceAliasDecl *getNextRedeclarationImpl() override; + NamespaceAliasDecl *getPreviousDeclImpl() override; + NamespaceAliasDecl *getMostRecentDeclImpl() override; friend class ASTDeclReader; public: + static NamespaceAliasDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation NamespaceLoc, + SourceLocation AliasLoc, + IdentifierInfo *Alias, + NestedNameSpecifierLoc QualifierLoc, + SourceLocation IdentLoc, + NamedDecl *Namespace); + + static NamespaceAliasDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + typedef redeclarable_base::redecl_range redecl_range; + typedef redeclarable_base::redecl_iterator redecl_iterator; + using redeclarable_base::redecls_begin; + using redeclarable_base::redecls_end; + using redeclarable_base::redecls; + using redeclarable_base::getPreviousDecl; + using redeclarable_base::getMostRecentDecl; + + NamespaceAliasDecl *getCanonicalDecl() override { + return getFirstDecl(); + } + const NamespaceAliasDecl *getCanonicalDecl() const { + return getFirstDecl(); + } + /// \brief Retrieve the nested-name-specifier that qualifies the /// name of the namespace, with source-location information. NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } @@ -2701,16 +2732,6 @@ public: /// may either be a NamespaceDecl or a NamespaceAliasDecl. NamedDecl *getAliasedNamespace() const { return Namespace; } - static NamespaceAliasDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation NamespaceLoc, - SourceLocation AliasLoc, - IdentifierInfo *Alias, - NestedNameSpecifierLoc QualifierLoc, - SourceLocation IdentLoc, - NamedDecl *Namespace); - - static NamespaceAliasDecl *CreateDeserialized(ASTContext &C, unsigned ID); - SourceRange getSourceRange() const override LLVM_READONLY { return SourceRange(NamespaceLoc, IdentLoc); } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1cd372b5f6..600c5a3a81 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3887,6 +3887,10 @@ def err_redefinition_different_type : Error< "redefinition of %0 with a different type%diff{: $ vs $|}1,2">; def err_redefinition_different_kind : Error< "redefinition of %0 as different kind of symbol">; +def err_redefinition_different_namespace_alias : Error< + "redefinition of %0 as an alias for a different namespace">; +def note_previous_namespace_alias : Note< + "previously defined as an alias for %0">; def warn_forward_class_redefinition : Warning< "redefinition of forward class %0 of a typedef name of an object type is ignored">, InGroup>; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 0de15a538c..e0801fb391 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1970,6 +1970,16 @@ NamespaceDecl *NamespaceDecl::getMostRecentDeclImpl() { void NamespaceAliasDecl::anchor() { } +NamespaceAliasDecl *NamespaceAliasDecl::getNextRedeclarationImpl() { + return getNextRedeclaration(); +} +NamespaceAliasDecl *NamespaceAliasDecl::getPreviousDeclImpl() { + return getPreviousDecl(); +} +NamespaceAliasDecl *NamespaceAliasDecl::getMostRecentDeclImpl() { + return getMostRecentDecl(); +} + NamespaceAliasDecl *NamespaceAliasDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation UsingLoc, SourceLocation AliasLoc, @@ -1977,15 +1987,16 @@ NamespaceAliasDecl *NamespaceAliasDecl::Create(ASTContext &C, DeclContext *DC, NestedNameSpecifierLoc QualifierLoc, SourceLocation IdentLoc, NamedDecl *Namespace) { + // FIXME: Preserve the aliased namespace as written. if (NamespaceDecl *NS = dyn_cast_or_null(Namespace)) Namespace = NS->getOriginalNamespace(); - return new (C, DC) NamespaceAliasDecl(DC, UsingLoc, AliasLoc, Alias, + return new (C, DC) NamespaceAliasDecl(C, DC, UsingLoc, AliasLoc, Alias, QualifierLoc, IdentLoc, Namespace); } NamespaceAliasDecl * NamespaceAliasDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) NamespaceAliasDecl(nullptr, SourceLocation(), + return new (C, ID) NamespaceAliasDecl(C, nullptr, SourceLocation(), SourceLocation(), nullptr, NestedNameSpecifierLoc(), SourceLocation(), nullptr); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 5da6546105..858f2c70b0 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -8264,49 +8264,50 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, return NewND; } -Decl *Sema::ActOnNamespaceAliasDef(Scope *S, - SourceLocation NamespaceLoc, - SourceLocation AliasLoc, - IdentifierInfo *Alias, - CXXScopeSpec &SS, - SourceLocation IdentLoc, - IdentifierInfo *Ident) { +Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, + SourceLocation AliasLoc, + IdentifierInfo *Alias, CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *Ident) { // Lookup the namespace name. LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); LookupParsedName(R, S, &SS); + if (R.isAmbiguous()) + return nullptr; + + if (R.empty()) { + if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { + Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + return nullptr; + } + } + assert(!R.isAmbiguous() && !R.empty()); + // Check if we have a previous declaration with the same name. - NamedDecl *PrevDecl - = LookupSingleName(S, Alias, AliasLoc, LookupOrdinaryName, - ForRedeclaration); + NamedDecl *PrevDecl = LookupSingleName(S, Alias, AliasLoc, LookupOrdinaryName, + ForRedeclaration); if (PrevDecl && !isDeclInScope(PrevDecl, CurContext, S)) PrevDecl = nullptr; if (PrevDecl) { if (NamespaceAliasDecl *AD = dyn_cast(PrevDecl)) { // We already have an alias with the same name that points to the same - // namespace, so don't create a new one. - // FIXME: At some point, we'll want to create the (redundant) - // declaration to maintain better source information. - if (!R.isAmbiguous() && !R.empty() && - AD->getNamespace()->Equals(getNamespaceDecl(R.getFoundDecl()))) + // namespace; check that it matches. + if (!AD->getNamespace()->Equals(getNamespaceDecl(R.getFoundDecl()))) { + Diag(AliasLoc, diag::err_redefinition_different_namespace_alias) + << Alias; + Diag(PrevDecl->getLocation(), diag::note_previous_namespace_alias) + << AD->getNamespace(); return nullptr; - } - - unsigned DiagID = isa(PrevDecl) ? diag::err_redefinition : - diag::err_redefinition_different_kind; - Diag(AliasLoc, DiagID) << Alias; - Diag(PrevDecl->getLocation(), diag::note_previous_definition); - return nullptr; - } - - if (R.isAmbiguous()) - return nullptr; - - if (R.empty()) { - if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { - Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + } + } else { + unsigned DiagID = isa(PrevDecl) + ? diag::err_redefinition + : diag::err_redefinition_different_kind; + Diag(AliasLoc, DiagID) << Alias; + Diag(PrevDecl->getLocation(), diag::note_previous_definition); return nullptr; } } @@ -8315,6 +8316,8 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, NamespaceAliasDecl::Create(Context, CurContext, NamespaceLoc, AliasLoc, Alias, SS.getWithLocInContext(Context), IdentLoc, R.getFoundDecl()); + if (PrevDecl) + AliasDecl->setPreviousDecl(cast(PrevDecl)); PushOnScopeChains(AliasDecl, S); return AliasDecl; diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index ba20f281ea..13393225b6 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -151,7 +151,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { switch (static_cast(Kind)) { case Decl::TranslationUnit: // Special case of a "merged" declaration. case Decl::Namespace: - case Decl::NamespaceAlias: // FIXME: Not yet redeclarable, but will be. + case Decl::NamespaceAlias: case Decl::Typedef: case Decl::TypeAlias: case Decl::Enum: @@ -189,8 +189,6 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::MSProperty: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: - case Decl::ImplicitParam: - case Decl::ParmVar: case Decl::NonTypeTemplateParm: case Decl::TemplateTemplateParm: case Decl::Using: @@ -213,6 +211,12 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Import: case Decl::OMPThreadPrivate: return false; + + // These indirectly derive from Redeclarable but are not actually + // redeclarable. + case Decl::ImplicitParam: + case Decl::ParmVar: + return false; } llvm_unreachable("Unhandled declaration kind"); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 9a5723058a..1c52b20096 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1205,11 +1205,13 @@ void ASTDeclReader::VisitNamespaceDecl(NamespaceDecl *D) { } void ASTDeclReader::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + RedeclarableResult Redecl = VisitRedeclarable(D); VisitNamedDecl(D); D->NamespaceLoc = ReadSourceLocation(Record, Idx); D->IdentLoc = ReadSourceLocation(Record, Idx); D->QualifierLoc = Reader.ReadNestedNameSpecifierLoc(F, Record, Idx); D->Namespace = ReadDeclAs(Record, Idx); + mergeRedeclarable(D, Redecl); } void ASTDeclReader::VisitUsingDecl(UsingDecl *D) { @@ -2355,10 +2357,10 @@ static bool isSameTemplateParameterList(const TemplateParameterList *X, /// \brief Determine whether the two declarations refer to the same entity. static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { assert(X->getDeclName() == Y->getDeclName() && "Declaration name mismatch!"); - + if (X == Y) return true; - + // Must be in the same context. if (!X->getDeclContext()->getRedeclContext()->Equals( Y->getDeclContext()->getRedeclContext())) @@ -2370,11 +2372,11 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { if (TypedefNameDecl *TypedefY = dyn_cast(Y)) return X->getASTContext().hasSameType(TypedefX->getUnderlyingType(), TypedefY->getUnderlyingType()); - + // Must have the same kind. if (X->getKind() != Y->getKind()) return false; - + // Objective-C classes and protocols with the same name always match. if (isa(X) || isa(X)) return true; @@ -2396,8 +2398,8 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { } // Functions with the same type and linkage match. - // FIXME: This needs to cope with function template specializations, - // merging of prototyped/non-prototyped functions, etc. + // FIXME: This needs to cope with merging of prototyped/non-prototyped + // functions, etc. if (FunctionDecl *FuncX = dyn_cast(X)) { FunctionDecl *FuncY = cast(Y); return (FuncX->getLinkageInternal() == FuncY->getLinkageInternal()) && @@ -2430,7 +2432,6 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { // Fields with the same name and the same type match. if (FieldDecl *FDX = dyn_cast(X)) { FieldDecl *FDY = cast(Y); - // FIXME: Diagnose if the types don't match. // FIXME: Also check the bitwidth is odr-equivalent, if any. return X->getASTContext().hasSameType(FDX->getType(), FDY->getType()); } @@ -2446,6 +2447,12 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { return USX->getTargetDecl() == USY->getTargetDecl(); } + // Namespace alias definitions with the same target match. + if (auto *NAX = dyn_cast(X)) { + auto *NAY = cast(Y); + return NAX->getNamespace()->Equals(NAY->getNamespace()); + } + // FIXME: Many other cases to implement. return false; } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index cc656402d9..0549851cbf 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -951,6 +951,7 @@ void ASTDeclWriter::VisitNamespaceDecl(NamespaceDecl *D) { } void ASTDeclWriter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + VisitRedeclarable(D); VisitNamedDecl(D); Writer.AddSourceLocation(D->getNamespaceLoc(), Record); Writer.AddSourceLocation(D->getTargetNameLoc(), Record); diff --git a/test/Modules/Inputs/cxx-decls-imported.h b/test/Modules/Inputs/cxx-decls-imported.h index 691b374d0d..8c1e74f17c 100644 --- a/test/Modules/Inputs/cxx-decls-imported.h +++ b/test/Modules/Inputs/cxx-decls-imported.h @@ -44,3 +44,6 @@ typedef struct { } NameForLinkage2; auto name_for_linkage2_inner_a = NameForLinkage2::Inner(); typedef decltype(name_for_linkage2_inner_a) NameForLinkage2Inner; + +namespace Aliased { extern int a; } +namespace Alias = Aliased; diff --git a/test/Modules/Inputs/cxx-decls-merged.h b/test/Modules/Inputs/cxx-decls-merged.h index 7f2baf36ee..86e81a9bb6 100644 --- a/test/Modules/Inputs/cxx-decls-merged.h +++ b/test/Modules/Inputs/cxx-decls-merged.h @@ -22,3 +22,6 @@ typedef struct { } NameForLinkage2; auto name_for_linkage2_inner_b = NameForLinkage2::Inner(); typedef decltype(name_for_linkage2_inner_b) NameForLinkage2Inner; + +namespace Aliased { extern int b; } +namespace Alias = Aliased; diff --git a/test/Modules/cxx-decls.cpp b/test/Modules/cxx-decls.cpp index 1fb550ba28..109306ed79 100644 --- a/test/Modules/cxx-decls.cpp +++ b/test/Modules/cxx-decls.cpp @@ -46,6 +46,8 @@ int overrides_virtual_functions_test = void use_extern_c_function() { ExternCFunction(); } +int use_namespace_alias() { return Alias::a + Alias::b; } + @import cxx_decls_premerged; void use_extern_c_function_2() { ExternCFunction(); } diff --git a/test/SemaCXX/namespace-alias.cpp b/test/SemaCXX/namespace-alias.cpp index e18b58b4d4..63615ecbd3 100644 --- a/test/SemaCXX/namespace-alias.cpp +++ b/test/SemaCXX/namespace-alias.cpp @@ -35,12 +35,12 @@ namespace H { namespace A2 { } // These all point to A1. - namespace B = A1; // expected-note {{previous definition is here}} + namespace B = A1; namespace B = A1; namespace C = B; - namespace B = C; + namespace B = C; // expected-note {{previously defined as an alias for 'A1'}} - namespace B = A2; // expected-error {{redefinition of 'B' as different kind of symbol}} + namespace B = A2; // expected-error {{redefinition of 'B' as an alias for a different namespace}} } namespace I { -- 2.50.1