[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.
This commit is contained in:
@@ -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
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -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.
|
||||
// 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 (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;
|
||||
if (Cxx20Enumerator) {
|
||||
Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator)
|
||||
<< SS.getRange();
|
||||
return false;
|
||||
}
|
||||
|
||||
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++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.
|
||||
|
||||
llvm::SmallPtrSet<const CXXRecordDecl *, 4> Bases;
|
||||
auto Collect = [&Bases](const CXXRecordDecl *Base) {
|
||||
Bases.insert(Base);
|
||||
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,
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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}}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,6 +36,15 @@ namespace cwg400 { // cwg400: 2.7
|
||||
// expected-error@-1 {{member 'a' found in multiple base classes of different 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
|
||||
|
||||
Reference in New Issue
Block a user