Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -530,8 +530,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< Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -5545,7 +5545,9 @@ 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, Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -12006,12 +12006,15 @@ 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()) { @@ -12065,6 +12068,11 @@ 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 @@ -12165,15 +12173,19 @@ 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(); + } } } + UsingDecl *UD = BuildValid(); @@ -12318,44 +12330,61 @@ /// 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. +/// 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) @@ -12372,7 +12401,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; @@ -12385,7 +12414,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. @@ -12404,12 +12433,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 @@ -12421,6 +12448,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. @@ -12506,6 +12535,7 @@ return true; } + Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, MultiTemplateParamsArg TemplateParamLists, SourceLocation UsingLoc, UnqualifiedId &Name, Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ 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); Index: clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp =================================================================== --- clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp +++ clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp @@ -17,6 +17,7 @@ }; class C { +public: int g(); }; Index: clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp =================================================================== --- /dev/null +++ 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 +}; +} + +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}} +} +#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 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 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 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 +}