Author: Yanzuo Liu Date: 2025-06-30T19:00:52+08:00 New Revision: 7a3e555353c5a5efd3978f731e7f98fbf75f53d7
URL: https://github.com/llvm/llvm-project/commit/7a3e555353c5a5efd3978f731e7f98fbf75f53d7 DIFF: https://github.com/llvm/llvm-project/commit/7a3e555353c5a5efd3978f731e7f98fbf75f53d7.diff LOG: [Clang][Sema] Require `BaseClass::` (not other classes) in member using-declaration in C++98 mode (#143492) [CWG400](https://wg21.link/cwg400) rejects member using-declaration whose nested-name-specifier doesn't refer to a base class of the current class. ```cpp struct A {}; struct B { using B::A; // error }; ``` Clang didn't reject this case in C++98 mode. This patch fixes this issue. Added: Modified: clang/docs/ReleaseNotes.rst clang/lib/Sema/SemaDeclCXX.cpp clang/test/CXX/class.access/class.access.dcl/p1.cpp clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp clang/test/CXX/drs/cwg4xx.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 05ac5e2e679e7..52012b4422da4 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -71,6 +71,11 @@ C++ Specific Potentially Breaking Changes if it's out of range. The Boost numeric_conversion library is impacted by this; it was fixed in Boost 1.81. (#GH143034) +- Fully implemented `CWG400 Using-declarations and the ` + `"struct hack" <https://wg21.link/CWG400>`_. Invalid member using-declaration + whose nested-name-specifier doesn't refer to a base class such as + ``using CurrentClass::Foo;`` is now rejected in C++98 mode. + ABI Changes in This Version --------------------------- diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c492e5f3f2dba..e8c65025bfe6d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -13619,82 +13619,40 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename, RequireCompleteDeclContext(const_cast<CXXScopeSpec&>(SS), NamedContext)) return true; - if (getLangOpts().CPlusPlus11) { - // C++11 [namespace.udecl]p3: - // In a using-declaration used as a member-declaration, the - // nested-name-specifier shall name a base class of the class - // being defined. - - if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom( - cast<CXXRecordDecl>(NamedContext))) { - - if (Cxx20Enumerator) { - Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator) - << SS.getRange(); - return false; - } - - if (CurContext == NamedContext) { - Diag(SS.getBeginLoc(), - diag::err_using_decl_nested_name_specifier_is_current_class) - << SS.getRange(); - return !getLangOpts().CPlusPlus20; - } - - if (!cast<CXXRecordDecl>(NamedContext)->isInvalidDecl()) { - Diag(SS.getBeginLoc(), - diag::err_using_decl_nested_name_specifier_is_not_base_class) - << SS.getScopeRep() << cast<CXXRecordDecl>(CurContext) - << SS.getRange(); - } - return true; + // C++26 [namespace.udecl]p3: + // In a using-declaration used as a member-declaration, each + // using-declarator shall either name an enumerator or have a + // nested-name-specifier naming a base class of the current class + // ([expr.prim.this]). ... + // "have a nested-name-specifier naming a base class of the current class" + // was introduced by CWG400. + + if (cast<CXXRecordDecl>(CurContext) + ->isProvablyNotDerivedFrom(cast<CXXRecordDecl>(NamedContext))) { + + if (Cxx20Enumerator) { + Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator) + << SS.getRange(); + return false; } - return false; - } - - // C++03 [namespace.udecl]p4: - // A using-declaration used as a member-declaration shall refer - // to a member of a base class of the class being defined [etc.]. - - // Salient point: SS doesn't have to name a base class as long as - // lookup only finds members from base classes. Therefore we can - // diagnose here only if we can prove that can't happen, - // i.e. if the class hierarchies provably don't intersect. - - // TODO: it would be nice if "definitely valid" results were cached - // in the UsingDecl and UsingShadowDecl so that these checks didn't - // need to be repeated. + if (CurContext == NamedContext) { + Diag(SS.getBeginLoc(), + diag::err_using_decl_nested_name_specifier_is_current_class) + << SS.getRange(); + return !getLangOpts().CPlusPlus20; + } - llvm::SmallPtrSet<const CXXRecordDecl *, 4> Bases; - auto Collect = [&Bases](const CXXRecordDecl *Base) { - Bases.insert(Base); + if (!cast<CXXRecordDecl>(NamedContext)->isInvalidDecl()) { + Diag(SS.getBeginLoc(), + diag::err_using_decl_nested_name_specifier_is_not_base_class) + << SS.getScopeRep() << cast<CXXRecordDecl>(CurContext) + << SS.getRange(); + } return true; - }; - - // Collect all bases. Return false if we find a dependent base. - if (!cast<CXXRecordDecl>(CurContext)->forallBases(Collect)) - return false; - - // Returns true if the base is dependent or is one of the accumulated base - // classes. - auto IsNotBase = [&Bases](const CXXRecordDecl *Base) { - return !Bases.count(Base); - }; - - // Return false if the class has a dependent base or if it or one - // of its bases is present in the base set of the current context. - if (Bases.count(cast<CXXRecordDecl>(NamedContext)) || - !cast<CXXRecordDecl>(NamedContext)->forallBases(IsNotBase)) - return false; - - Diag(SS.getRange().getBegin(), - diag::err_using_decl_nested_name_specifier_is_not_base_class) - << SS.getScopeRep() - << cast<CXXRecordDecl>(CurContext) - << SS.getRange(); + } - return true; + return false; } Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, diff --git a/clang/test/CXX/class.access/class.access.dcl/p1.cpp b/clang/test/CXX/class.access/class.access.dcl/p1.cpp index fdb1373dd9b12..b7ee6c9a7c04b 100644 --- a/clang/test/CXX/class.access/class.access.dcl/p1.cpp +++ b/clang/test/CXX/class.access/class.access.dcl/p1.cpp @@ -318,26 +318,23 @@ namespace test4 { // expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}} #endif - C::foo; // legal in C++03 + C::foo; #if __cplusplus <= 199711L // expected-warning@-2 {{access declarations are deprecated; use using declarations instead}} #else // expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}} - // expected-error@-5 {{using declaration refers to its own class}} #endif + // expected-error@-6 {{using declaration refers to its own class}} - Subclass::foo; // legal in C++03 + Subclass::foo; #if __cplusplus <= 199711L // expected-warning@-2 {{access declarations are deprecated; use using declarations instead}} #else // expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}} - // expected-error@-5 {{using declaration refers into 'Subclass', which is not a base class of 'C'}} #endif + // expected-error@-6 {{using declaration refers into 'Subclass', which is not a base class of 'C'}} int bar(); -#if __cplusplus <= 199711L - //expected-note@-2 {{target of using declaration}} -#endif C::bar; #if __cplusplus <= 199711L // expected-warning@-2 {{access declarations are deprecated; use using declarations instead}} 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 index 657695657e0f7..bcc26bf4f4356 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp @@ -30,17 +30,10 @@ class D2 : public B { using B::x; using C::g; // expected-error{{using declaration refers into 'C', which is not a base class of 'D2'}} - // These are valid in C++98 but not in C++11. - using D::f2; - using D::E2; - using D::e2; - using D::x2; -#if __cplusplus >= 201103L - // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} - // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} - // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} - // expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}} -#endif + using D::f2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}} + using D::E2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}} + using D::e2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}} + using D::x2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}} using B::EC; using B::EC::ec; // expected-warning {{a C++20 extension}} expected-warning 0-1 {{C++11}} @@ -71,13 +64,7 @@ namespace test1 { using Base::bar; // expected-error {{no member named 'bar'}} using Unrelated::foo; // expected-error {{not a base class}} - // In C++98, it's hard to see that these are invalid, because indirect - // references to base class members are permitted. - using C::foo; - using Subclass::foo; -#if __cplusplus >= 201103L - // expected-error@-3 {{refers to its own class}} - // expected-error@-3 {{not a base class}} -#endif + using C::foo; // expected-error {{refers to its own class}} + using Subclass::foo; // expected-error {{not a base class}} }; } diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp index 973cd77279f19..26b66350b86d9 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp @@ -206,17 +206,9 @@ namespace test4 { using InnerNS::foo; // expected-error {{not a class}} using Base::bar; // expected-error {{no member named 'bar'}} using Unrelated::foo; // expected-error {{not a base class}} - using C::foo; // legal in C++03 - using Subclass::foo; // legal in C++03 -#if __cplusplus >= 201103L - // expected-error@-3 {{refers to its own class}} - // expected-error@-3 {{refers into 'Subclass', which is not a base class}} -#endif - + using C::foo; // expected-error {{refers to its own class}} + using Subclass::foo; // expected-error {{refers into 'Subclass', which is not a base class}} int bar(); -#if __cplusplus < 201103L - // expected-note@-2 {{target of using declaration}} -#endif using C::bar; // expected-error {{refers to its own class}} }; } diff --git a/clang/test/CXX/drs/cwg4xx.cpp b/clang/test/CXX/drs/cwg4xx.cpp index 210f7ae71ec04..dc53ae712a0b5 100644 --- a/clang/test/CXX/drs/cwg4xx.cpp +++ b/clang/test/CXX/drs/cwg4xx.cpp @@ -36,6 +36,15 @@ namespace cwg400 { // cwg400: 2.7 // expected-error@-1 {{member 'a' found in multiple base classes of diff erent types}} // expected-note@#cwg400-A {{member type 'cwg400::A::a' found by ambiguous name lookup}} // expected-note@#cwg400-B {{member type 'cwg400::B::a' found by ambiguous name lookup}} + struct F : A {}; + struct G : A { + using G::A; + // expected-error@-1 {{using declaration refers to its own class}} + using G::a; + // expected-error@-1 {{using declaration refers to its own class}} + using F::a; + // expected-error@-1 {{using declaration refers into 'F', which is not a base class of 'G'}} + }; } // namespace cwg400 namespace cwg401 { // cwg401: 2.8 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
