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

Reply via email to