diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -538,8 +538,8 @@ "a const variable|a constexpr variable}0 instead">; def err_using_decl_can_not_refer_to_namespace : Error< "using declaration cannot refer to a namespace">; -def err_using_decl_can_not_refer_to_scoped_enum : Error< - "using declaration cannot refer to a scoped enumerator">; +def err_cxx14_using_decl_can_not_refer_to_scoped_enum : Error< + "using declaration cannot refer to a scoped enumerator before C++20">; def err_using_decl_constructor : Error< "using declaration cannot refer to a constructor">; def warn_cxx98_compat_using_decl_constructor : Warning< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5673,11 +5673,12 @@ const CXXScopeSpec &SS, SourceLocation NameLoc, const LookupResult &Previous); - bool CheckUsingDeclQualifier(SourceLocation UsingLoc, - bool HasTypename, + bool CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename, const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, - SourceLocation NameLoc); + SourceLocation NameLoc, + const LookupResult *R = nullptr, + const UsingDecl *UD = nullptr); NamedDecl *BuildUsingDeclaration( Scope *S, AccessSpecifier AS, SourceLocation UsingLoc, diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -12069,12 +12069,14 @@ SS, IdentLoc, Previous)) return nullptr; - // Check for bad qualifiers. - if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo, - IdentLoc)) + DeclContext *LookupContext = computeDeclContext(SS); + + // Check for bad qualifiers. This will not necessarily detect all badness, as + // C++20 permits enumerators to cross the class-heirarchy boundary + if (!LookupContext && CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, + SS, NameInfo, IdentLoc)) return nullptr; - DeclContext *LookupContext = computeDeclContext(SS); NamedDecl *D; NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); if (!LookupContext || EllipsisLoc.isValid()) { @@ -12128,12 +12130,18 @@ LookupQualifiedName(R, LookupContext); + // Validate the context, now we have a lookup + if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo, + IdentLoc, &R)) + return nullptr; + // Try to correct typos if possible. If constructor name lookup finds no // results, that means the named class has no explicit constructors, and we // suppressed declaring implicit ones (probably because it's dependent or // invalid). if (R.empty() && NameInfo.getName().getNameKind() != DeclarationName::CXXConstructorName) { + // FIXME: 2021-04-07: When was this hack needed? Can it be deleted now? // HACK: Work around a bug in libstdc++'s detection of ::gets. Sometimes // it will believe that glibc provides a ::gets in cases where it does not, // and will try to pull it into namespace std with a using-declaration. @@ -12228,13 +12236,16 @@ return BuildInvalid(); } - // C++14 [namespace.udecl]p7: - // A using-declaration shall not name a scoped enumerator. - if (auto *ED = R.getAsSingle()) { - if (cast(ED->getDeclContext())->isScoped()) { - Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum) - << SS.getRange(); - return BuildInvalid(); + if (!getLangOpts().CPlusPlus20) { + // C++14 [namespace.udecl]p7: + // A using-declaration shall not name a scoped enumerator. + // C++20 p1099 permits enumerators. + if (auto *ED = R.getAsSingle()) { + if (cast(ED->getDeclContext())->isScoped()) { + Diag(IdentLoc, diag::err_cxx14_using_decl_can_not_refer_to_scoped_enum) + << SS.getRange(); + return BuildInvalid(); + } } } @@ -12377,48 +12388,62 @@ return false; } - /// Checks that the given nested-name qualifier used in a using decl /// 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, +/// R is nullptr, if the caller has not (yet) done a lookup, otherwise it's the +/// result of that lookup. UD is likewise nullptr, except when we have an +/// already-populated UsingDecl whose shadow decls contain the same information +/// (i.e. we're instantiating a UsingDecl with non-dependent scope). +bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename, const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, - SourceLocation NameLoc) { + SourceLocation NameLoc, + const LookupResult *R, const UsingDecl *UD) { DeclContext *NamedContext = computeDeclContext(SS); + assert(bool(NamedContext) == (R || UD) && !(R && UD) && + "resolvable context must have exactly one set of decls"); + + if (getLangOpts().CPlusPlus20) { + // There could be a type-tag and an enum. There must only be an enum + EnumConstantDecl *ED = nullptr; + if (R) + ED = R->getAsSingle(); + else if (UD && UD->shadow_size() == 1) + ED = dyn_cast(UD->shadow_begin()->getTargetDecl()); + + if (ED) + // Naming an enumerator is ok, regardless of heirarchy. We check + // accessibility elsewhere. + return false; + } if (!CurContext->isRecord()) { // C++03 [namespace.udecl]p3: // C++0x [namespace.udecl]p8: // A using-declaration for a class member shall be a member-declaration. + // C++20 [namespace.udecl]p7 + // ... other than an enumerator ... // 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; - if (RD && RequireCompleteDeclContext(const_cast(SS), RD)) - RD = nullptr; - + if (NamedContext ? NamedContext->getRedeclContext()->isRecord() + : HasTypename) { Diag(NameLoc, diag::err_using_decl_can_not_refer_to_class_member) << SS.getRange(); - // If we have a complete, non-dependent source type, try to suggest a - // way to get the same effect. - if (!RD) + if (!NamedContext) return true; - // Find what this using-declaration was referring to. - LookupResult R(*this, NameInfo, LookupOrdinaryName); - R.setHideTags(false); - R.suppressDiagnostics(); - LookupQualifiedName(R, RD); + auto *RD = cast(NamedContext->getRedeclContext()); + if (RequireCompleteDeclContext(const_cast(SS), RD)) + return true; - if (R.getAsSingle()) { + if (!R) { + // We will have already diagnosed the problem on the template + // definition, Maybe we should do so again? + } else if (R->getAsSingle()) { if (getLangOpts().CPlusPlus11) { // Convert 'using X::Y;' to 'using Y = X::Y;'. Diag(SS.getBeginLoc(), diag::note_using_decl_class_member_workaround) @@ -12435,7 +12460,7 @@ << FixItHint::CreateInsertion( InsertLoc, " " + NameInfo.getName().getAsString()); } - } else if (R.getAsSingle()) { + } else if (R->getAsSingle()) { // Don't provide a fixit outside C++11 mode; we don't want to suggest // repeating the type of the static data member here. FixItHint FixIt; @@ -12448,7 +12473,7 @@ Diag(UsingLoc, diag::note_using_decl_class_member_workaround) << 2 // reference declaration << FixIt; - } else if (R.getAsSingle()) { + } else if (R->getAsSingle()) { // Don't provide a fixit outside C++11 mode; we don't want to suggest // repeating the type of the enumeration here, and we can't do so if // the type is anonymous. @@ -12467,12 +12492,10 @@ return true; } - // Otherwise, this might be valid. + // Otherwise ok return false; } - // The current scope is a record. - // If the named context is dependent, we can't decide much. if (!NamedContext) { // FIXME: in C++0x, we can diagnose if we can prove that the @@ -12484,6 +12507,8 @@ return false; } + // The current scope is a record. + if (!NamedContext->isRecord()) { // Ideally this would point at the last name in the specifier, // but we don't have that level of source info. diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3063,10 +3063,9 @@ } if (!NewUD->isInvalidDecl() && - SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), - SS, NameInfo, D->getLocation())) + SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), SS, + NameInfo, D->getLocation(), nullptr, D)) NewUD->setInvalidDecl(); - SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D); NewUD->setAccess(D->getAccess()); Owner->addDecl(NewUD); @@ -3075,6 +3074,9 @@ if (NewUD->isInvalidDecl()) return NewUD; + // If the using scope was dependent, or we had dependent bases, we need to + // recheck the inheritance + if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName) SemaRef.CheckInheritingConstructorUsingDecl(NewUD); diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp @@ -17,6 +17,7 @@ }; class C { +public: int g(); }; diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp @@ -0,0 +1,149 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s + +// p1099 'using SCOPEDENUM::MEMBER;' + +namespace Bob { +enum class Kevin { + Stuart, + AlsoStuart +#if __cplusplus >= 202002L +// expected-note@-3{{target of using declaration}} +// expected-note@-3{{target of using declaration}} +#endif +}; +} // namespace Bob + +using Bob::Kevin::Stuart; +#if __cplusplus < 202002L +// expected-error@-2{{using declaration cannot refer to a scoped enumerator}} +#else +using Bob::Kevin::Stuart; + +auto b = Stuart; + +namespace Foo { +int Stuart; // expected-note{{conflicting declaration}} +using Bob::Kevin::Stuart; // expected-error{{target of using declaration conflicts}} + +using Bob::Kevin::AlsoStuart; // expected-note{{using declaration}} +int AlsoStuart; // expected-error{{declaration conflicts with target}} +} // namespace Foo +#endif + +namespace One { + +// derived from [namespace.udecl]/3 +enum class button { up, + down }; +struct S { + using button::up; +#if __cplusplus < 202002L + // expected-error@-2{{using declaration in class}} +#else + button b = up; +#endif +}; + +#if __cplusplus >= 202002L +// some more +struct T : S { + button c = up; +}; +#endif +enum E2 { e2 }; +} // namespace One + +namespace Two { +enum class E1 { e1 }; + +struct S { + using One::e2; +#if __cplusplus < 202002L + // expected-error@-2{{using declaration in class}} +#else + One::E2 c = e2; +#endif +}; + +} // namespace Two + +namespace Three { + +enum E3 { e3 }; +struct e3; + +struct S { + using Three::e3; // expected-error{{using declaration in class}} + + enum class E4 { e4 }; + enum E5 { e5 }; +}; + +using S::e5; +using S::E4::e4; +#if __cplusplus < 202002L +// expected-error@-3{{using declaration cannot refer to class member}} +// expected-note@-4{{use a constexpr variable instead}} +// expected-error@-4{{using declaration cannot refer to a scoped enumerator}} +#else +auto a = e4; +auto b = e5; +#endif +} // namespace Three + +namespace Four { + +template +struct TPL { + enum class E1 { e1 }; + struct IN { + enum class E2 { e2 }; + }; + +protected: + enum class E3 { e3 }; // expected-note{{declared protected here}} +}; + +using TPL::E1::e1; +#if __cplusplus < 202002L +// expected-error@-2{{cannot refer to a scoped enumerator}} +#else +using TPL::IN::E2::e2; + +auto a = e1; +auto b = e2; +#endif + +enum class E4 { e4 }; +template +struct DER : TPL { + using TPL::E1::e1; +#if __cplusplus < 202002L + // expected-error@-2{{'TPL::E1::', which is not a class}} + // expected-error@-3{{'TPL::E1::', which is not a class}} +#endif + using TPL::E3::e3; // expected-error{{is a protected member}} +#if __cplusplus < 202002L + // expected-error@-2{{'TPL::E3::', which is not a class}} + // expected-error@-3{{'TPL::E3::', which is not a class}} +#endif + + using E4::e4; +#if __cplusplus < 202002L + // expected-error@-2{{which is not a class}} +#else + auto Foo() { return e1; } + auto Bar() { return e2; } +#endif +}; + +DER x; // expected-note{{requested here}} +DER y; +#if __cplusplus < 202002L +// expected-note@-2{{requested here}} +#else +auto y1 = y.Foo(); +auto y2 = y.Bar(); +#endif +} // namespace Four