Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -539,8 +539,9 @@ "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_using_decl_scoped_enumerator : ExtWarn< + "using declaration naming a scoped enumerator is a C++20 extension">, + InGroup; 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 @@ -5677,11 +5677,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, Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -12069,15 +12069,16 @@ SS, IdentLoc, Previous)) return nullptr; - // Check for bad qualifiers. - if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo, - IdentLoc)) - return nullptr; - DeclContext *LookupContext = computeDeclContext(SS); + NamedDecl *D; NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); if (!LookupContext || EllipsisLoc.isValid()) { + // Dependent scope, or an unexpanded pack + if (!LookupContext && CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, + SS, NameInfo, IdentLoc)) + return nullptr; + if (HasTypenameKeyword) { // FIXME: not all declaration name kinds are legal here D = UnresolvedUsingTypenameDecl::Create(Context, CurContext, @@ -12128,6 +12129,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 @@ -12227,15 +12233,15 @@ 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_using_decl_scoped_enumerator) << SS.getRange(); + } } - } UsingDecl *UD = BuildValid(); @@ -12376,48 +12382,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; - + // dependent class or enumeration scope. If we have a 'typename' keyword, + // the scope must resolve to a class type. + 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) @@ -12434,7 +12454,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; @@ -12447,7 +12467,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. @@ -12466,12 +12486,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 @@ -12483,6 +12501,7 @@ 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. 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++17 -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-warning@-2{{using declaration naming a scoped enumerator is a C++20 extension}} +#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-warning@-4{{using declaration naming a scoped enumerator is a C++20 extension}} +#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-warning@-2{{using declaration naming a scoped enumerator is a C++20 extension}} +#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 Index: clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp =================================================================== --- clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp +++ clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp @@ -1,4 +1,11 @@ // RUN: %clang_cc1 -std=c++11 -verify %s +// RUN: %clang_cc1 -std=c++17 -verify %s +// RUN: %clang_cc1 -std=c++20 -verify %s enum class EC { ec }; -using EC::ec; // expected-error {{using declaration cannot refer to a scoped enumerator}} +using EC::ec; +#if __cplusplus < 202002 +// expected-warning@-2 {{using declaration naming a scoped enumerator is a C++20 extension}} +#else +// expected-no-diagnostics +#endif Index: clang/test/SemaCXX/enum-scoped.cpp =================================================================== --- clang/test/SemaCXX/enum-scoped.cpp +++ clang/test/SemaCXX/enum-scoped.cpp @@ -301,8 +301,8 @@ int E::*p; // expected-error {{does not point into a class}} using E::f; // expected-error {{no member named 'f'}} - using E::a; // expected-error {{using declaration cannot refer to a scoped enumerator}} - E b = a; // expected-error {{undeclared}} + using E::a; // expected-warning {{using declaration naming a scoped enumerator is a C++20 extension}} + E b = a; } namespace test11 {