Skip to content

Commit 7a3e555

Browse files
authored
[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.
1 parent c3f8dd1 commit 7a3e555

File tree

6 files changed

+55
-107
lines changed

6 files changed

+55
-107
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ C++ Specific Potentially Breaking Changes
7171
if it's out of range. The Boost numeric_conversion library is impacted by
7272
this; it was fixed in Boost 1.81. (#GH143034)
7373

74+
- Fully implemented `CWG400 Using-declarations and the `
75+
`"struct hack" <https://wg21.link/CWG400>`_. Invalid member using-declaration
76+
whose nested-name-specifier doesn't refer to a base class such as
77+
``using CurrentClass::Foo;`` is now rejected in C++98 mode.
78+
7479
ABI Changes in This Version
7580
---------------------------
7681

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 29 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -13619,82 +13619,40 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
1361913619
RequireCompleteDeclContext(const_cast<CXXScopeSpec&>(SS), NamedContext))
1362013620
return true;
1362113621

13622-
if (getLangOpts().CPlusPlus11) {
13623-
// C++11 [namespace.udecl]p3:
13624-
// In a using-declaration used as a member-declaration, the
13625-
// nested-name-specifier shall name a base class of the class
13626-
// being defined.
13627-
13628-
if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(
13629-
cast<CXXRecordDecl>(NamedContext))) {
13630-
13631-
if (Cxx20Enumerator) {
13632-
Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator)
13633-
<< SS.getRange();
13634-
return false;
13635-
}
13636-
13637-
if (CurContext == NamedContext) {
13638-
Diag(SS.getBeginLoc(),
13639-
diag::err_using_decl_nested_name_specifier_is_current_class)
13640-
<< SS.getRange();
13641-
return !getLangOpts().CPlusPlus20;
13642-
}
13643-
13644-
if (!cast<CXXRecordDecl>(NamedContext)->isInvalidDecl()) {
13645-
Diag(SS.getBeginLoc(),
13646-
diag::err_using_decl_nested_name_specifier_is_not_base_class)
13647-
<< SS.getScopeRep() << cast<CXXRecordDecl>(CurContext)
13648-
<< SS.getRange();
13649-
}
13650-
return true;
13622+
// C++26 [namespace.udecl]p3:
13623+
// In a using-declaration used as a member-declaration, each
13624+
// using-declarator shall either name an enumerator or have a
13625+
// nested-name-specifier naming a base class of the current class
13626+
// ([expr.prim.this]). ...
13627+
// "have a nested-name-specifier naming a base class of the current class"
13628+
// was introduced by CWG400.
13629+
13630+
if (cast<CXXRecordDecl>(CurContext)
13631+
->isProvablyNotDerivedFrom(cast<CXXRecordDecl>(NamedContext))) {
13632+
13633+
if (Cxx20Enumerator) {
13634+
Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator)
13635+
<< SS.getRange();
13636+
return false;
1365113637
}
1365213638

13653-
return false;
13654-
}
13655-
13656-
// C++03 [namespace.udecl]p4:
13657-
// A using-declaration used as a member-declaration shall refer
13658-
// to a member of a base class of the class being defined [etc.].
13659-
13660-
// Salient point: SS doesn't have to name a base class as long as
13661-
// lookup only finds members from base classes. Therefore we can
13662-
// diagnose here only if we can prove that can't happen,
13663-
// i.e. if the class hierarchies provably don't intersect.
13664-
13665-
// TODO: it would be nice if "definitely valid" results were cached
13666-
// in the UsingDecl and UsingShadowDecl so that these checks didn't
13667-
// need to be repeated.
13639+
if (CurContext == NamedContext) {
13640+
Diag(SS.getBeginLoc(),
13641+
diag::err_using_decl_nested_name_specifier_is_current_class)
13642+
<< SS.getRange();
13643+
return !getLangOpts().CPlusPlus20;
13644+
}
1366813645

13669-
llvm::SmallPtrSet<const CXXRecordDecl *, 4> Bases;
13670-
auto Collect = [&Bases](const CXXRecordDecl *Base) {
13671-
Bases.insert(Base);
13646+
if (!cast<CXXRecordDecl>(NamedContext)->isInvalidDecl()) {
13647+
Diag(SS.getBeginLoc(),
13648+
diag::err_using_decl_nested_name_specifier_is_not_base_class)
13649+
<< SS.getScopeRep() << cast<CXXRecordDecl>(CurContext)
13650+
<< SS.getRange();
13651+
}
1367213652
return true;
13673-
};
13674-
13675-
// Collect all bases. Return false if we find a dependent base.
13676-
if (!cast<CXXRecordDecl>(CurContext)->forallBases(Collect))
13677-
return false;
13678-
13679-
// Returns true if the base is dependent or is one of the accumulated base
13680-
// classes.
13681-
auto IsNotBase = [&Bases](const CXXRecordDecl *Base) {
13682-
return !Bases.count(Base);
13683-
};
13684-
13685-
// Return false if the class has a dependent base or if it or one
13686-
// of its bases is present in the base set of the current context.
13687-
if (Bases.count(cast<CXXRecordDecl>(NamedContext)) ||
13688-
!cast<CXXRecordDecl>(NamedContext)->forallBases(IsNotBase))
13689-
return false;
13690-
13691-
Diag(SS.getRange().getBegin(),
13692-
diag::err_using_decl_nested_name_specifier_is_not_base_class)
13693-
<< SS.getScopeRep()
13694-
<< cast<CXXRecordDecl>(CurContext)
13695-
<< SS.getRange();
13653+
}
1369613654

13697-
return true;
13655+
return false;
1369813656
}
1369913657

1370013658
Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,

clang/test/CXX/class.access/class.access.dcl/p1.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,26 +318,23 @@ namespace test4 {
318318
// expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}}
319319
#endif
320320

321-
C::foo; // legal in C++03
321+
C::foo;
322322
#if __cplusplus <= 199711L
323323
// expected-warning@-2 {{access declarations are deprecated; use using declarations instead}}
324324
#else
325325
// expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}}
326-
// expected-error@-5 {{using declaration refers to its own class}}
327326
#endif
327+
// expected-error@-6 {{using declaration refers to its own class}}
328328

329-
Subclass::foo; // legal in C++03
329+
Subclass::foo;
330330
#if __cplusplus <= 199711L
331331
// expected-warning@-2 {{access declarations are deprecated; use using declarations instead}}
332332
#else
333333
// expected-error@-4 {{ISO C++11 does not allow access declarations; use using declarations instead}}
334-
// expected-error@-5 {{using declaration refers into 'Subclass', which is not a base class of 'C'}}
335334
#endif
335+
// expected-error@-6 {{using declaration refers into 'Subclass', which is not a base class of 'C'}}
336336

337337
int bar();
338-
#if __cplusplus <= 199711L
339-
//expected-note@-2 {{target of using declaration}}
340-
#endif
341338
C::bar;
342339
#if __cplusplus <= 199711L
343340
// expected-warning@-2 {{access declarations are deprecated; use using declarations instead}}

clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,10 @@ class D2 : public B {
3030
using B::x;
3131
using C::g; // expected-error{{using declaration refers into 'C', which is not a base class of 'D2'}}
3232

33-
// These are valid in C++98 but not in C++11.
34-
using D::f2;
35-
using D::E2;
36-
using D::e2;
37-
using D::x2;
38-
#if __cplusplus >= 201103L
39-
// expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}}
40-
// expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}}
41-
// expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}}
42-
// expected-error@-5 {{using declaration refers into 'D', which is not a base class of 'D2'}}
43-
#endif
33+
using D::f2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}}
34+
using D::E2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}}
35+
using D::e2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}}
36+
using D::x2; // expected-error {{using declaration refers into 'D', which is not a base class of 'D2'}}
4437

4538
using B::EC;
4639
using B::EC::ec; // expected-warning {{a C++20 extension}} expected-warning 0-1 {{C++11}}
@@ -71,13 +64,7 @@ namespace test1 {
7164
using Base::bar; // expected-error {{no member named 'bar'}}
7265
using Unrelated::foo; // expected-error {{not a base class}}
7366

74-
// In C++98, it's hard to see that these are invalid, because indirect
75-
// references to base class members are permitted.
76-
using C::foo;
77-
using Subclass::foo;
78-
#if __cplusplus >= 201103L
79-
// expected-error@-3 {{refers to its own class}}
80-
// expected-error@-3 {{not a base class}}
81-
#endif
67+
using C::foo; // expected-error {{refers to its own class}}
68+
using Subclass::foo; // expected-error {{not a base class}}
8269
};
8370
}

clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,17 +206,9 @@ namespace test4 {
206206
using InnerNS::foo; // expected-error {{not a class}}
207207
using Base::bar; // expected-error {{no member named 'bar'}}
208208
using Unrelated::foo; // expected-error {{not a base class}}
209-
using C::foo; // legal in C++03
210-
using Subclass::foo; // legal in C++03
211-
#if __cplusplus >= 201103L
212-
// expected-error@-3 {{refers to its own class}}
213-
// expected-error@-3 {{refers into 'Subclass', which is not a base class}}
214-
#endif
215-
209+
using C::foo; // expected-error {{refers to its own class}}
210+
using Subclass::foo; // expected-error {{refers into 'Subclass', which is not a base class}}
216211
int bar();
217-
#if __cplusplus < 201103L
218-
// expected-note@-2 {{target of using declaration}}
219-
#endif
220212
using C::bar; // expected-error {{refers to its own class}}
221213
};
222214
}

clang/test/CXX/drs/cwg4xx.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ namespace cwg400 { // cwg400: 2.7
3636
// expected-error@-1 {{member 'a' found in multiple base classes of different types}}
3737
// expected-note@#cwg400-A {{member type 'cwg400::A::a' found by ambiguous name lookup}}
3838
// expected-note@#cwg400-B {{member type 'cwg400::B::a' found by ambiguous name lookup}}
39+
struct F : A {};
40+
struct G : A {
41+
using G::A;
42+
// expected-error@-1 {{using declaration refers to its own class}}
43+
using G::a;
44+
// expected-error@-1 {{using declaration refers to its own class}}
45+
using F::a;
46+
// expected-error@-1 {{using declaration refers into 'F', which is not a base class of 'G'}}
47+
};
3948
} // namespace cwg400
4049

4150
namespace cwg401 { // cwg401: 2.8

0 commit comments

Comments
 (0)