From ecd4ae9e89f6e216cf6bf9fe08ad04901ecca631 Mon Sep 17 00:00:00 2001 From: Richard Smith <richard-llvm@metafoo.co.uk> Date: Thu, 5 Dec 2013 04:30:04 +0000 Subject: [PATCH] Per [dcl.meaning]p1, a name in an inline namespace can be redeclared using a name from the enclosing namespace set if the name is specified as a qualified-id. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@196464 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/IdentifierResolver.h | 11 ++-- include/clang/Sema/Sema.h | 11 ++-- lib/Sema/IdentifierResolver.cpp | 7 +-- lib/Sema/SemaDecl.cpp | 38 ++++++------ lib/Sema/SemaTemplate.cpp | 3 +- test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp | 79 +++++++++++++++++++++++++ 6 files changed, 115 insertions(+), 34 deletions(-) diff --git a/include/clang/Sema/IdentifierResolver.h b/include/clang/Sema/IdentifierResolver.h index 99d94a1fed8..abe974c6f76 100644 --- a/include/clang/Sema/IdentifierResolver.h +++ b/include/clang/Sema/IdentifierResolver.h @@ -150,11 +150,14 @@ public: /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. /// - /// \param ExplicitInstantiationOrSpecialization When true, we are checking - /// whether the declaration is in scope for the purposes of explicit template - /// instantiation or specialization. The default is false. + /// \param AllowInlineNamespace If \c true, we are checking whether a prior + /// declaration is in scope in a declaration that requires a prior + /// declaration (because it is either explicitly qualified or is a + /// template instantiation or specialization). In this case, a + /// declaration is in scope if it's in the inline namespace set of the + /// context. bool isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S = 0, - bool ExplicitInstantiationOrSpecialization = false) const; + bool AllowInlineNamespace = false) const; /// AddDecl - Link the decl to its shadowed decl chain. void AddDecl(NamedDecl *D); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 2fc6a4dc1e9..ece5140550b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1823,11 +1823,11 @@ public: /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. /// - /// \param ExplicitInstantiationOrSpecialization When true, we are checking - /// whether the declaration is in scope for the purposes of explicit template - /// instantiation or specialization. The default is false. + /// \param AllowInlineNamespace If \c true, allow the declaration to be in the + /// enclosing namespace set of the context, rather than contained + /// directly within it. bool isDeclInScope(NamedDecl *D, DeclContext *Ctx, Scope *S = 0, - bool ExplicitInstantiationOrSpecialization = false); + bool AllowInlineNamespace = false); /// Finds the scope corresponding to the given decl context, if it /// happens to be an enclosing scope. Otherwise return NULL. @@ -2528,8 +2528,7 @@ public: AssociatedClassSet &AssociatedClasses); void FilterLookupForScope(LookupResult &R, DeclContext *Ctx, Scope *S, - bool ConsiderLinkage, - bool ExplicitInstantiationOrSpecialization); + bool ConsiderLinkage, bool AllowInlineNamespace); void DiagnoseAmbiguousLookup(LookupResult &Result); //@} diff --git a/lib/Sema/IdentifierResolver.cpp b/lib/Sema/IdentifierResolver.cpp index 6e354b9060e..2298f3ad681 100644 --- a/lib/Sema/IdentifierResolver.cpp +++ b/lib/Sema/IdentifierResolver.cpp @@ -95,7 +95,7 @@ IdentifierResolver::~IdentifierResolver() { /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S, - bool ExplicitInstantiationOrSpecialization) const { + bool AllowInlineNamespace) const { Ctx = Ctx->getRedeclContext(); if (Ctx->isFunctionOrMethod() || S->isFunctionPrototypeScope()) { @@ -131,9 +131,8 @@ bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S, } DeclContext *DCtx = D->getDeclContext()->getRedeclContext(); - return ExplicitInstantiationOrSpecialization - ? Ctx->InEnclosingNamespaceSetOf(DCtx) - : Ctx->Equals(DCtx); + return AllowInlineNamespace ? Ctx->InEnclosingNamespaceSetOf(DCtx) + : Ctx->Equals(DCtx); } /// AddDecl - Link the decl to its shadowed decl chain. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 07d7ceebdb8..065aba913ea 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1086,9 +1086,8 @@ void Sema::pushExternalDeclIntoScope(NamedDecl *D, DeclarationName Name) { } bool Sema::isDeclInScope(NamedDecl *D, DeclContext *Ctx, Scope *S, - bool ExplicitInstantiationOrSpecialization) { - return IdResolver.isDeclInScope(D, Ctx, S, - ExplicitInstantiationOrSpecialization); + bool AllowInlineNamespace) { + return IdResolver.isDeclInScope(D, Ctx, S, AllowInlineNamespace); } Scope *Sema::getScopeForDeclContext(Scope *S, DeclContext *DC) { @@ -1108,21 +1107,19 @@ static bool isOutOfScopePreviousDeclaration(NamedDecl *, /// Filters out lookup results that don't fall within the given scope /// as determined by isDeclInScope. -void Sema::FilterLookupForScope(LookupResult &R, - DeclContext *Ctx, Scope *S, +void Sema::FilterLookupForScope(LookupResult &R, DeclContext *Ctx, Scope *S, bool ConsiderLinkage, - bool ExplicitInstantiationOrSpecialization) { + bool AllowInlineNamespace) { LookupResult::Filter F = R.makeFilter(); while (F.hasNext()) { NamedDecl *D = F.next(); - if (isDeclInScope(D, Ctx, S, ExplicitInstantiationOrSpecialization)) + if (isDeclInScope(D, Ctx, S, AllowInlineNamespace)) continue; - if (ConsiderLinkage && - isOutOfScopePreviousDeclaration(D, Ctx, Context)) + if (ConsiderLinkage && isOutOfScopePreviousDeclaration(D, Ctx, Context)) continue; - + F.erase(); } @@ -4671,8 +4668,8 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD, LookupResult &Previous, bool &Redeclaration) { // Merge the decl with the existing one if appropriate. If the decl is // in an outer scope, it isn't the same thing. - FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage*/ false, - /*ExplicitInstantiationOrSpecialization=*/false); + FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage*/false, + /*AllowInlineNamespace*/false); filterNonConflictingPreviousDecls(Context, NewTD, Previous); if (!Previous.empty()) { Redeclaration = true; @@ -5400,15 +5397,16 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, } // Diagnose shadowed variables before filtering for scope. - if (!D.getCXXScopeSpec().isSet()) + if (D.getCXXScopeSpec().isEmpty()) CheckShadow(S, NewVD, Previous); // Don't consider existing declarations that are in a different // scope and are out-of-semantic-context declarations (if the new // declaration has linkage). - FilterLookupForScope( - Previous, OriginalDC, S, shouldConsiderLinkage(NewVD), - IsExplicitSpecialization || IsVariableTemplateSpecialization); + FilterLookupForScope(Previous, OriginalDC, S, shouldConsiderLinkage(NewVD), + D.getCXXScopeSpec().isNotEmpty() || + IsExplicitSpecialization || + IsVariableTemplateSpecialization); // Check whether the previous declaration is in the same block scope. This // affects whether we merge types with it, per C++11 [dcl.array]p3. @@ -6886,6 +6884,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Filter out previous declarations that don't match the scope. FilterLookupForScope(Previous, OriginalDC, S, shouldConsiderLinkage(NewFD), + D.getCXXScopeSpec().isNotEmpty() || isExplicitSpecialization || isFunctionTemplateSpecialization); @@ -10683,7 +10682,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // in the same scope (so that the definition/declaration completes or // rementions the tag), reuse the decl. if (TUK == TUK_Reference || TUK == TUK_Friend || - isDeclInScope(PrevDecl, SearchDC, S, isExplicitSpecialization)) { + isDeclInScope(PrevDecl, SearchDC, S, + SS.isNotEmpty() || isExplicitSpecialization)) { // Make sure that this wasn't declared as an enum and now used as a // struct or something similar. if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, @@ -10844,8 +10844,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, Invalid = true; // Otherwise, only diagnose if the declaration is in scope. - } else if (!isDeclInScope(PrevDecl, SearchDC, S, - isExplicitSpecialization)) { + } else if (!isDeclInScope(PrevDecl, SearchDC, S, + SS.isNotEmpty() || isExplicitSpecialization)) { // do nothing // Diagnose implicit declarations introduced by elaborated types. diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index b5b2f0dbedc..0376d106eae 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -968,7 +968,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, PrevDecl = (*Previous.begin())->getUnderlyingDecl(); } } - } else if (PrevDecl && !isDeclInScope(PrevDecl, SemanticContext, S)) + } else if (PrevDecl && + !isDeclInScope(PrevDecl, SemanticContext, S, SS.isValid())) PrevDecl = PrevClassTemplate = 0; if (PrevClassTemplate) { diff --git a/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp b/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp index 99334b845ab..13810769743 100644 --- a/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp +++ b/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp @@ -22,3 +22,82 @@ int decltype(tfoo<T>())::i; // expected-error{{nested name specifier 'decltype(t template<typename T> void decltype(tfoo<T>())::func() { // expected-error{{nested name specifier 'decltype(tfoo<T>())::' for declaration does not refer into a class, class template or class template partial specialization}} } + +// An init-declarator named with a qualified-id can refer to an element of the +// inline namespace set of the named namespace. +namespace inline_namespaces { + namespace N { + inline namespace M { + void f(); // expected-note {{possible target}} + void g(); + extern int m, n; + struct S; struct T; + enum E : int; enum F : int; + template<typename T> void ft(); // expected-note {{here}} + template<typename T> void gt(); // expected-note {{here}} + template<typename T> extern int mt; // expected-note {{here}} expected-warning {{extension}} + template<typename T> extern int nt; // expected-note {{here}} expected-warning {{extension}} + template<typename T> struct U; // expected-note {{here}} + template<typename T> struct V; // expected-note {{here}} + } + + // When named by unqualified-id, we do *not* look in the inline namespace + // set. + void f() {} // expected-note {{possible target}} + int m; + struct S {}; + enum E : int {}; + + static_assert(&f != &M::f, ""); // expected-error {{reference to overloaded function could not be resolved}} + static_assert(&m != &M::m, ""); + typedef S X; // expected-note {{previous}} + typedef M::S X; // expected-error {{different type}} + typedef E Y; // expected-note {{previous}} + typedef M::E Y; // expected-error {{different type}} + + // When named by (unqualified) template-id, we do look in the inline + // namespace set. See [namespace.def]p8, [temp.explicit]p3, + // [temp.expl.spec]p2. + // + // This is not explicitly specified for partial specializations, but + // that is just a language defect. + template<> void ft<int>() {} + template void ft<char>(); // expected-error {{undefined}} + + template<typename T> int mt<T*>; // expected-warning {{extension}} + template<> int mt<int>; // expected-warning {{extension}} + template int mt<int*>; + template int mt<char>; // expected-error {{undefined}} + + template<typename T> struct U<T*> {}; + template<> struct U<int> {}; + template struct U<int*>; + template struct U<char>; // expected-error {{undefined}} + } + + // When named by qualified-id, we *do* look in the inline namespace set. + void N::g() {} + int N::n; + struct N::T {}; + enum N::F : int {}; + + static_assert(&N::g == &N::M::g, ""); + static_assert(&N::n == &N::M::n, ""); + typedef N::T X; + typedef N::M::T X; + typedef N::F Y; + typedef N::M::F Y; + + template<> void N::gt<int>() {} + template void N::gt<char>(); // expected-error {{undefined}} + + template<typename T> int N::nt<T*>; // expected-warning {{extension}} + template<> int N::nt<int>; // expected-warning {{extension}} + template int N::nt<int*>; + template int N::nt<char>; // expected-error {{undefined}} + + template<typename T> struct N::V<T*> {}; + template<> struct N::V<int> {}; + template struct N::V<int*>; + template struct N::V<char>; // expected-error {{undefined}} +} -- GitLab