From d8a9e375582fea89906d89f7fca4cbf0b240346f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 18 Dec 2016 21:39:37 +0000 Subject: [PATCH] Fix some interactions between C++11 and C++14 features and using-declarations: * a dependent non-type using-declaration within a function template can be valid, as it can refer to an enumerator, so don't reject it in the template definition * we can partially substitute into a dependent using-declaration if it appears within a (local class in a) generic lambda within a function template, which means an UnresolvedUsing*Decl doesn't necessarily instantiate to a UsingDecl. llvm-svn: 290071 --- clang/include/clang/AST/ASTContext.h | 12 +- clang/include/clang/Sema/Sema.h | 1 + clang/lib/AST/ASTContext.cpp | 11 +- clang/lib/Sema/SemaDeclCXX.cpp | 50 +++++-- clang/lib/Sema/SemaOverload.cpp | 9 +- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 50 ++++--- .../basic.namespace/namespace.udecl/p8-cxx0x.cpp | 158 ++++++++++++++++++++- 7 files changed, 244 insertions(+), 47 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a07f412..f2aceab 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -398,11 +398,11 @@ private: llvm::DenseMap TemplateOrInstantiation; - /// \brief Keeps track of the declaration from which a UsingDecl was + /// \brief Keeps track of the declaration from which a using declaration was /// created during instantiation. /// - /// The source declaration is always a UsingDecl, an UnresolvedUsingValueDecl, - /// or an UnresolvedUsingTypenameDecl. + /// The source and target declarations are always a UsingDecl, an + /// UnresolvedUsingValueDecl, or an UnresolvedUsingTypenameDecl. /// /// For example: /// \code @@ -421,7 +421,7 @@ private: /// /// This mapping will contain an entry that maps from the UsingDecl in /// B to the UnresolvedUsingDecl in B. - llvm::DenseMap InstantiatedFromUsingDecl; + llvm::DenseMap InstantiatedFromUsingDecl; llvm::DenseMap InstantiatedFromUsingShadowDecl; @@ -849,11 +849,11 @@ public: /// \brief If the given using decl \p Inst is an instantiation of a /// (possibly unresolved) using decl from a template instantiation, /// return it. - NamedDecl *getInstantiatedFromUsingDecl(UsingDecl *Inst); + NamedDecl *getInstantiatedFromUsingDecl(NamedDecl *Inst); /// \brief Remember that the using decl \p Inst is an instantiation /// of the using decl \p Pattern of a class template. - void setInstantiatedFromUsingDecl(UsingDecl *Inst, NamedDecl *Pattern); + void setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern); void setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst, UsingShadowDecl *Pattern); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2b54d5ee..136f848 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4317,6 +4317,7 @@ public: SourceLocation NameLoc, const LookupResult &Previous); bool CheckUsingDeclQualifier(SourceLocation UsingLoc, + bool HasTypename, const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, SourceLocation NameLoc); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 7a98ac4..0ad663f 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1270,9 +1270,8 @@ void ASTContext::setClassScopeSpecializationPattern(FunctionDecl *FD, } NamedDecl * -ASTContext::getInstantiatedFromUsingDecl(UsingDecl *UUD) { - llvm::DenseMap::const_iterator Pos - = InstantiatedFromUsingDecl.find(UUD); +ASTContext::getInstantiatedFromUsingDecl(NamedDecl *UUD) { + auto Pos = InstantiatedFromUsingDecl.find(UUD); if (Pos == InstantiatedFromUsingDecl.end()) return nullptr; @@ -1280,11 +1279,15 @@ ASTContext::getInstantiatedFromUsingDecl(UsingDecl *UUD) { } void -ASTContext::setInstantiatedFromUsingDecl(UsingDecl *Inst, NamedDecl *Pattern) { +ASTContext::setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern) { assert((isa(Pattern) || isa(Pattern) || isa(Pattern)) && "pattern decl is not a using decl"); + assert((isa(Inst) || + isa(Inst) || + isa(Inst)) && + "instantiation did not produce a using decl"); assert(!InstantiatedFromUsingDecl[Inst] && "pattern already exists"); InstantiatedFromUsingDecl[Inst] = Pattern; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 8b1c234..4742995 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9010,8 +9010,23 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, F.done(); } else { assert(IsInstantiation && "no scope in non-instantiation"); - assert(CurContext->isRecord() && "scope not record in instantiation"); - LookupQualifiedName(Previous, CurContext); + if (CurContext->isRecord()) + LookupQualifiedName(Previous, CurContext); + else { + // No redeclaration check is needed here; in non-member contexts we + // diagnosed all possible conflicts with other using-declarations when + // building the template: + // + // For a dependent non-type using declaration, the only valid case is + // if we instantiate to a single enumerator. We check for conflicts + // between shadow declarations we introduce, and we check in the template + // definition for conflicts between a non-type using declaration and any + // other declaration, which together covers all cases. + // + // A dependent typename using declaration will never successfully + // instantiate, since it will always name a class member, so we reject + // that in the template definition. + } } // Check for invalid redeclarations. @@ -9020,7 +9035,8 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, return nullptr; // Check for bad qualifiers. - if (CheckUsingDeclQualifier(UsingLoc, SS, NameInfo, IdentLoc)) + if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo, + IdentLoc)) return nullptr; DeclContext *LookupContext = computeDeclContext(SS); @@ -9259,7 +9275,19 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc, = dyn_cast(D)) { DTypename = true; DQual = UD->getQualifier(); - } else continue; + } else if (!isa(D) && Qual->isDependent() && + !HasTypenameKeyword) { + // A dependent qualifier outside a class can only ever resolve to an + // enumeration type. Therefore it conflicts with any other non-type + // declaration in the same scope. + // FIXME: How should we check for dependent type-type conflicts at block + // scope? + Diag(NameLoc, diag::err_redefinition_different_kind) + << Prev.getLookupName(); + Diag(D->getLocation(), diag::note_previous_definition); + return true; + } + else continue; // using decls differ if one says 'typename' and the other doesn't. // FIXME: non-dependent using decls? @@ -9285,6 +9313,7 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc, /// in the current context is appropriately related to the current /// scope. If an error is found, diagnoses it and returns true. bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, + bool HasTypename, const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, SourceLocation NameLoc) { @@ -9295,9 +9324,11 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, // C++0x [namespace.udecl]p8: // A using-declaration for a class member shall be a member-declaration. - // If we weren't able to compute a valid scope, it must be a - // dependent class scope. - if (!NamedContext || NamedContext->getRedeclContext()->isRecord()) { + // If we weren't able to compute a valid scope, it might validly be a + // dependent class scope or a dependent enumeration unscoped scope. If + // we have a 'typename' keyword, the scope must resolve to a class type. + if ((HasTypename && !NamedContext) || + (NamedContext && NamedContext->getRedeclContext()->isRecord())) { auto *RD = NamedContext ? cast(NamedContext->getRedeclContext()) : nullptr; @@ -9357,7 +9388,8 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, if (getLangOpts().CPlusPlus11) { // Convert 'using X::Y;' to 'auto &Y = X::Y;'. FixIt = FixItHint::CreateReplacement( - UsingLoc, "constexpr auto " + NameInfo.getName().getAsString() + " = "); + UsingLoc, + "constexpr auto " + NameInfo.getName().getAsString() + " = "); } Diag(UsingLoc, diag::note_using_decl_class_member_workaround) @@ -9367,7 +9399,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, return true; } - // Otherwise, everything is known to be fine. + // Otherwise, this might be valid. return false; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 1899e41..d49142b 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -987,10 +987,17 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, assert(Old.getLookupKind() == LookupUsingDeclName); } else if (isa(OldD)) { // We can always overload with tags by hiding them. - } else if (isa(OldD)) { + } else if (auto *UUD = dyn_cast(OldD)) { // Optimistically assume that an unresolved using decl will // overload; if it doesn't, we'll have to diagnose during // template instantiation. + // + // Exception: if the scope is dependent and this is not a class + // member, the using declaration can only introduce an enumerator. + if (UUD->getQualifier()->isDependent() && !UUD->isCXXClassMember()) { + Match = *I; + return Ovl_NonFunction; + } } else { // (C++ 13p1): // Only function declarations can be overloaded; object and type diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 6b6abc7..0956a1c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2430,8 +2430,8 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) { } if (!NewUD->isInvalidDecl() && - SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), SS, NameInfo, - D->getLocation())) + SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), + SS, NameInfo, D->getLocation())) NewUD->setInvalidDecl(); SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D); @@ -2515,7 +2515,7 @@ Decl * TemplateDeclInstantiator /*instantiation*/ true, /*typename*/ true, D->getTypenameLoc()); if (UD) - SemaRef.Context.setInstantiatedFromUsingDecl(cast(UD), D); + SemaRef.Context.setInstantiatedFromUsingDecl(UD, D); return UD; } @@ -2539,7 +2539,7 @@ Decl * TemplateDeclInstantiator /*instantiation*/ true, /*typename*/ false, SourceLocation()); if (UD) - SemaRef.Context.setInstantiatedFromUsingDecl(cast(UD), D); + SemaRef.Context.setInstantiatedFromUsingDecl(UD, D); return UD; } @@ -4520,13 +4520,13 @@ static bool isInstantiationOf(UsingDecl *Pattern, } static bool isInstantiationOf(UnresolvedUsingValueDecl *Pattern, - UsingDecl *Instance, + NamedDecl *Instance, ASTContext &C) { return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern); } static bool isInstantiationOf(UnresolvedUsingTypenameDecl *Pattern, - UsingDecl *Instance, + NamedDecl *Instance, ASTContext &C) { return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern); } @@ -4550,15 +4550,13 @@ static bool isInstantiationOfStaticDataMember(VarDecl *Pattern, // D is the prospective pattern static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) { if (D->getKind() != Other->getKind()) { - if (UnresolvedUsingTypenameDecl *UUD - = dyn_cast(D)) { + if (auto *UUD = dyn_cast(D)) { if (UsingDecl *UD = dyn_cast(Other)) { return isInstantiationOf(UUD, UD, Ctx); } } - if (UnresolvedUsingValueDecl *UUD - = dyn_cast(D)) { + if (auto *UUD = dyn_cast(D)) { if (UsingDecl *UD = dyn_cast(Other)) { return isInstantiationOf(UUD, UD, Ctx); } @@ -4567,31 +4565,31 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) { return false; } - if (CXXRecordDecl *Record = dyn_cast(Other)) + if (auto *Record = dyn_cast(Other)) return isInstantiationOf(cast(D), Record); - if (FunctionDecl *Function = dyn_cast(Other)) + if (auto *Function = dyn_cast(Other)) return isInstantiationOf(cast(D), Function); - if (EnumDecl *Enum = dyn_cast(Other)) + if (auto *Enum = dyn_cast(Other)) return isInstantiationOf(cast(D), Enum); - if (VarDecl *Var = dyn_cast(Other)) + if (auto *Var = dyn_cast(Other)) if (Var->isStaticDataMember()) return isInstantiationOfStaticDataMember(cast(D), Var); - if (ClassTemplateDecl *Temp = dyn_cast(Other)) + if (auto *Temp = dyn_cast(Other)) return isInstantiationOf(cast(D), Temp); - if (FunctionTemplateDecl *Temp = dyn_cast(Other)) + if (auto *Temp = dyn_cast(Other)) return isInstantiationOf(cast(D), Temp); - if (ClassTemplatePartialSpecializationDecl *PartialSpec - = dyn_cast(Other)) + if (auto *PartialSpec = + dyn_cast(Other)) return isInstantiationOf(cast(D), PartialSpec); - if (FieldDecl *Field = dyn_cast(Other)) { + if (auto *Field = dyn_cast(Other)) { if (!Field->getDeclName()) { // This is an unnamed field. return declaresSameEntity(Ctx.getInstantiatedFromUnnamedFieldDecl(Field), @@ -4599,14 +4597,20 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) { } } - if (UsingDecl *Using = dyn_cast(Other)) + if (auto *Using = dyn_cast(Other)) return isInstantiationOf(cast(D), Using, Ctx); - if (UsingShadowDecl *Shadow = dyn_cast(Other)) + if (auto *Using = dyn_cast(Other)) + return isInstantiationOf(cast(D), Using, Ctx); + + if (auto *Using = dyn_cast(Other)) + return isInstantiationOf(cast(D), Using, Ctx); + + if (auto *Shadow = dyn_cast(Other)) return isInstantiationOf(cast(D), Shadow, Ctx); - return D->getDeclName() && isa(Other) && - D->getDeclName() == cast(Other)->getDeclName(); + return D->getDeclName() && + D->getDeclName() == cast(Other)->getDeclName(); } template diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p8-cxx0x.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p8-cxx0x.cpp index 6c63f061..8f6638e 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p8-cxx0x.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p8-cxx0x.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify %s // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s // RUN: not %clang_cc1 -fsyntax-only -std=c++98 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --check-prefix=CXX98 %s // RUN: not %clang_cc1 -fsyntax-only -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --check-prefix=CXX11 %s // C++0x N2914. @@ -44,10 +45,159 @@ void f() { #endif } -template -struct PR21933 : T { - static void StaticFun() { using T::member; } // expected-error{{using declaration cannot refer to class member}} -}; +namespace PR21933 { + struct A { int member; }; + struct B { static int member; }; + enum C { member }; + + template + struct X { + static void StaticFun() { + using T::member; // expected-error 2{{class member}} expected-note {{use a reference instead}} +#if __cplusplus < 201103L + // expected-error@-2 {{cannot be used prior to '::'}} +#endif + (void)member; + } + }; + template + struct Y : T { + static void StaticFun() { + using T::member; // expected-error 2{{class member}} expected-note {{use a reference instead}} + (void)member; + } + }; + + void f() { + X::StaticFun(); // expected-note {{instantiation of}} + X::StaticFun(); // expected-note {{instantiation of}} + X::StaticFun(); +#if __cplusplus < 201103L + // expected-note@-2 {{instantiation of}} +#endif + Y::StaticFun(); // expected-note {{instantiation of}} + Y::StaticFun(); // expected-note {{instantiation of}} + } + + template void value_vs_value() { + using T::a; // expected-note {{previous}} +#if __cplusplus < 201103L + // expected-error@-2 {{cannot be used prior to '::'}} +#endif + extern int a(); // expected-error {{different kind of symbol}} + a(); + + extern int b(); + using T::b; + b(); + + using T::c; + using U::c; + c(); + } + + template void value_vs_type() { + using T::Xt; // expected-note {{previous}} + typedef struct {} Xt; // expected-error {{different kind of symbol}} + (void)Xt; + + using T::Xs; // expected-note {{candidate}} + struct Xs {}; // expected-note {{candidate}} + // FIXME: This is wrong, the using declaration hides the type. + Xs xs; // expected-error {{ambiguous}} + + using T::Xe; // expected-note {{candidate}} + enum Xe {}; // expected-note {{candidate}} + // FIXME: This is wrong, the using declaration hides the type. + Xe xe; // expected-error {{ambiguous}} + + typedef struct {} Yt; // expected-note {{candidate}} + using T::Yt; // eypected-error {{different kind of symbol}} expected-note {{candidate}} + Yt yt; // expected-error {{ambiguous}} + + struct Ys {}; // expected-note {{candidate}} + using T::Ys; // expected-note {{candidate}} + // FIXME: This is wrong, the using declaration hides the type. + Ys ys; // expected-error {{ambiguous}} + + enum Ye {}; // expected-note {{candidate}} + using T::Ye; // expected-note {{candidate}} + // FIXME: This is wrong, the using declaration hides the type. + Ye ye; // expected-error {{ambiguous}} + } + + template void type() { + // Must be a class member because T:: can only name a class or enum, + // and an enum cannot have a type member. + using typename T::X; // expected-error {{cannot refer to class member}} + } + + namespace N1 { enum E { a, b, c }; } + namespace N2 { enum E { a, b, c }; } + void g() { value_vs_value(); } +#if __cplusplus < 201103L + // expected-note@-2 {{in instantiation of}} +#endif + +#if __cplusplus >= 201402L + namespace partial_substitute { + template auto f() { + return [](auto x) { + using A = typename T::template U; + using A::E::e; + struct S : A { + using A::f; + using typename A::type; + type f(int) { return e; } + }; + return S(); + }; + } + enum Enum { e }; + struct X { + template struct U { + int f(int, int); + using type = int; + using E = Enum; + }; + }; + int test() { + auto s = f()(0); + return s.f(0) + s.f(0, 0); + } + + template auto g() { + return [](auto x) { + using X = decltype(x); + struct S : T::template Q, U::template Q { + using T::template Q::f; + using U::template Q::f; + void h() { f(); } + void h(int n) { f(n); } + }; + return S(); + }; + } + struct A { template struct Q { int f(); }; }; + struct B { template struct Q { int f(int); }; }; + int test2() { + auto s = g()(0); + s.f(); + s.f(0); + s.h(); + s.h(0); + } + } +#endif + + template struct RepeatedMember : T, U { + // FIXME: This is the wrong error: we should complain that a member type + // cannot be redeclared at class scope. + using typename T::type; // expected-note {{candidate}} + using typename U::type; // expected-note {{candidate}} + type x; // expected-error {{ambiguous}} + }; +} struct S { static int n; -- 2.7.4