randyli created this revision. randyli added reviewers: doug.gregor, rjmccall, rsmith, dblaikie, tigerleapgorge. Herald added a project: All. randyli requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
try to fix bug #54406 Per [class.access.base]/6, this example is ill-formed: struct A { int m; } a; struct B : A {}; int n = a.B::m; ... because the object expression a cannot be implicitly converted to B (because B is not a base class of A). Clang does not diagnose this and accepts the member access. This opens a hole in the protected member access rules: struct C { protected: int m; }; struct D : private C { int get(C &c) { return c.D::m; } }; This example is invalid for the same reason, but Clang accepts it. ([class.protected]p1, which normally would prevent D from accessing protected members on a C object that is not a D object, does not apply here because m as a member of the naming class D is private, not protected.) Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D122460 Files: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaExprMember.cpp clang/test/CXX/class.access/class.protected/p1.cpp clang/test/CXX/drs/dr1xx.cpp clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp clang/test/SemaCXX/member-expr.cpp clang/test/SemaCXX/qual-id-test.cpp clang/test/SemaTemplate/typo-dependent-name.cpp
Index: clang/test/SemaTemplate/typo-dependent-name.cpp =================================================================== --- clang/test/SemaTemplate/typo-dependent-name.cpp +++ clang/test/SemaTemplate/typo-dependent-name.cpp @@ -37,9 +37,9 @@ bool f(T other) { // We can determine that 'inner' does not exist at parse time, so can // perform typo correction in this case. - return this->inner<other>::z; // expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}} + return this->inner<other>::z; // expected-error {{Y<Q>::Inner<0>' is not a base of 'Y<Q>'}} expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}} } }; struct Q { constexpr operator int() { return 0; } }; -void use_y(Y<Q> x) { x.f(Q()); } +void use_y(Y<Q> x) { x.f(Q()); } // expected-note {{in instantiation of member function 'Y<Q>::f' requested here}} Index: clang/test/SemaCXX/qual-id-test.cpp =================================================================== --- clang/test/SemaCXX/qual-id-test.cpp +++ clang/test/SemaCXX/qual-id-test.cpp @@ -54,7 +54,7 @@ a.A::sub::x(); a.A::B::base::x(); - a.bad::x(); // expected-error{{'bad::x' is not a member of class 'A::sub'}} + a.bad::x(); // expected-error{{'bad' is not a base of 'sub'}} a->foo(); a->member::foo(); @@ -75,7 +75,7 @@ a->A::sub::x(); a->A::B::base::x(); - a->bad::x(); // expected-error{{'bad::x' is not a member of class 'A::sub'}} + a->bad::x(); // expected-error{{'bad' is not a base of 'sub'}} (*a)->foo(); (*a)->member::foo(); @@ -119,7 +119,7 @@ a.A::B::base::x(); a->A::member::foo(); - a.bad::x(); // expected-error{{'bad::x' is not a member of class 'A::sub'}} + a.bad::x(); // expected-error{{'bad' is not a base of 'sub'}} } void test_fun5() { Index: clang/test/SemaCXX/member-expr.cpp =================================================================== --- clang/test/SemaCXX/member-expr.cpp +++ clang/test/SemaCXX/member-expr.cpp @@ -117,8 +117,8 @@ void f(Y *y) { y->N::X1<int>; // expected-error{{'rdar8231724::N::X1' is not a member of class 'rdar8231724::Y'}} - y->Z<int>::n; // expected-error{{'rdar8231724::Z<int>::n' is not a member of class 'rdar8231724::Y'}} - y->template Z<int>::n; // expected-error{{'rdar8231724::Z<int>::n' is not a member of class 'rdar8231724::Y'}} + y->Z<int>::n; // expected-error{{'rdar8231724::Z<int>' is not a base of 'Y'}} + y->template Z<int>::n; // expected-error{{'rdar8231724::Z<int>' is not a base of 'Y'}} #if __cplusplus <= 199711L // C++03 or earlier modes // expected-warning@-2{{'template' keyword outside of a template}} #endif Index: clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp =================================================================== --- clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp +++ clang/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp @@ -30,7 +30,7 @@ decltype(outer::middle::inner()) a; void scope() { - a.decltype(outer::middle())::mfunc(); // expected-error{{'PR10127::outer::middle::mfunc' is not a member of class 'decltype(outer::middle::inner())'}} + a.decltype(outer::middle())::mfunc(); // expected-error{{PR10127::outer::middle' is not a base of 'inner'}} a.decltype(outer::middle::inner())::func(); a.decltype(outer::middle())::inner::func(); a.decltype(outer())::middle::inner::func(); Index: clang/test/CXX/drs/dr1xx.cpp =================================================================== --- clang/test/CXX/drs/dr1xx.cpp +++ clang/test/CXX/drs/dr1xx.cpp @@ -477,7 +477,7 @@ namespace dr141 { // dr141: yes template<typename T> void f(); - template<typename T> struct S { int n; }; // expected-note {{'::dr141::S<int>::n' declared here}} + template<typename T> struct S { int n; }; struct A : S<int> { template<typename T> void f(); template<typename T> struct S {}; @@ -485,7 +485,7 @@ struct B : S<int> {} b; void g() { a.f<int>(); - (void)a.S<int>::n; // expected-error {{no member named 'n' in 'dr141::A::S<int>'; did you mean '::dr141::S<int>::n'?}} + (void)a.S<int>::n; // expected-error {{'dr141::A::S<int>' is not a base of 'A'}} #if __cplusplus < 201103L // expected-error@-2 {{ambiguous}} // expected-note@-11 {{lookup from the current scope}} Index: clang/test/CXX/class.access/class.protected/p1.cpp =================================================================== --- clang/test/CXX/class.access/class.protected/p1.cpp +++ clang/test/CXX/class.access/class.protected/p1.cpp @@ -323,31 +323,32 @@ namespace test9 { class A { // expected-note {{member is declared here}} - protected: int foo(); // expected-note 4 {{declared}} expected-note 3 {{can only access this member on an object of type}} expected-note 2 {{member is declared here}} + protected: int foo(); // expected-note 2 {{can only access this member on an object of type 'test9::D'}} \ + // expected-note 2 {{member is declared here}} }; class B : public A { // expected-note {{member is declared here}} friend class D; }; - class C : protected B { // expected-note {{declared}} \ - // expected-note 7 {{constrained}} + class C : protected B { // expected-note 5 {{constrained by protected inheritance here}} expected-note {{declared protected here}} + }; class D : public A { static void test(A &a) { a.foo(); // expected-error {{'foo' is a protected member}} a.A::foo(); // expected-error {{'foo' is a protected member}} - a.B::foo(); // expected-error {{'foo' is a protected member}} - a.C::foo(); // expected-error {{'foo' is a protected member}} - a.D::foo(); // expected-error {{'foo' is a protected member}} + a.B::foo(); // expected-error {{'test9::B' is not a base of 'A'}} + a.C::foo(); // expected-error {{'test9::C' is not a base of 'A'}} + a.D::foo(); // expected-error {{'test9::D' is not a base of 'A'}} } static void test(B &b) { b.foo(); b.A::foo(); b.B::foo(); // accessible as named in A - b.C::foo(); // expected-error {{'foo' is a protected member}} + b.C::foo(); // expected-error {{'test9::C' is not a base of 'B'}} } static void test(C &c) { @@ -362,8 +363,8 @@ static void test(D &d) { d.foo(); d.A::foo(); - d.B::foo(); - d.C::foo(); // expected-error {{'foo' is a protected member}} + d.B::foo(); // expected-error {{'test9::B' is not a base of 'D'}} + d.C::foo(); // expected-error {{'test9::C' is not a base of 'D'}} } }; } Index: clang/lib/Sema/SemaExprMember.cpp =================================================================== --- clang/lib/Sema/SemaExprMember.cpp +++ clang/lib/Sema/SemaExprMember.cpp @@ -680,6 +680,22 @@ << DC << SS.getRange(); return true; } + // fix bug #54406 + // Per [class.access.base]/6, this example is ill-formed: + // struct A { + // int m; + // } a; + // struct B : A {}; + // int n = a.B::m; + if (BaseExpr && isa<CXXRecordDecl>(DC) && isa<CXXRecordDecl>(RDecl)) { + CXXRecordDecl *SRecord = cast<CXXRecordDecl>(DC)->getCanonicalDecl(); + CXXRecordDecl *RRecord = cast<CXXRecordDecl>(RDecl)->getCanonicalDecl(); + if (SRecord != RRecord && RRecord->isProvablyNotDerivedFrom(SRecord)) { + SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_not_base) + << DC << RDecl << SS.getRange(); + return true; + } + } } // The record definition is complete, now look up the member. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1798,6 +1798,8 @@ "lookup from the current scope refers here">; def err_qualified_member_nonclass : Error< "qualified member access refers to a member in %0">; +def err_qualified_member_not_base : Error< + "%0 is not a base of %1">; def err_incomplete_member_access : Error< "member access into incomplete type %0">; def err_incomplete_type : Error<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits