aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2021-01-08 10:02:04 -0500
committerPatrick Palka <ppalka@redhat.com>2021-01-08 10:02:04 -0500
commit98a1fb705ead9258642f2dec0431f11508a9b13c (patch)
tree84788f3c172bcaa2186c829f65a5efa4e8602346
parent76be18f442948d1a4bc49a7d670b07097f9e5983 (diff)
downloadgcc-98a1fb705ead9258642f2dec0431f11508a9b13c.zip
gcc-98a1fb705ead9258642f2dec0431f11508a9b13c.tar.gz
gcc-98a1fb705ead9258642f2dec0431f11508a9b13c.tar.bz2
c++: Fix access checking of scoped non-static member [PR98515]
In the first testcase below, we incorrectly reject the use of the protected non-static member A::var0 from C<int>::g() because check_accessibility_of_qualified_id, at template parse time, determines that the access doesn't go through 'this'. (This happens because the dependent base B<T> of C<T> doesn't have a binfo object, so it appears to DERIVED_FROM_P that A is not an indirect base of C<T>.) From there we create the corresponding deferred access check, which we then perform at instantiation time and which (expectedly) fails. The problem ultimately seems to be that we can't in general determine whether a use of a scoped non-static member goes through 'this' until instantiation time, as the second testcase below illustrates. So this patch makes check_accessibility_of_qualified_id punt in such situations to avoid creating a bogus deferred access check. gcc/cp/ChangeLog: PR c++/98515 * semantics.c (check_accessibility_of_qualified_id): Punt if we're checking access of a scoped non-static member inside a class template. gcc/testsuite/ChangeLog: PR c++/98515 * g++.dg/template/access32.C: New test. * g++.dg/template/access33.C: New test.
-rw-r--r--gcc/cp/semantics.c20
-rw-r--r--gcc/testsuite/g++.dg/template/access32.C8
-rw-r--r--gcc/testsuite/g++.dg/template/access33.C9
3 files changed, 32 insertions, 5 deletions
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index b448efe..c6b4c70 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2107,14 +2107,24 @@ check_accessibility_of_qualified_id (tree decl,
/* If the reference is to a non-static member of the
current class, treat it as if it were referenced through
`this'. */
- tree ct;
if (DECL_NONSTATIC_MEMBER_P (decl)
- && current_class_ptr
- && DERIVED_FROM_P (scope, ct = current_nonlambda_class_type ()))
- qualifying_type = ct;
+ && current_class_ptr)
+ if (tree current = current_nonlambda_class_type ())
+ {
+ if (dependent_type_p (current))
+ /* In general we can't know whether this access goes through
+ `this' until instantiation time. Punt now, or else we might
+ create a deferred access check that's not relative to `this'
+ when it ought to be. We'll check this access again after
+ substitution, e.g. from tsubst_qualified_id. */
+ return true;
+
+ if (DERIVED_FROM_P (scope, current))
+ qualifying_type = current;
+ }
/* Otherwise, use the type indicated by the
nested-name-specifier. */
- else
+ if (!qualifying_type)
qualifying_type = nested_name_specifier;
}
else
diff --git a/gcc/testsuite/g++.dg/template/access32.C b/gcc/testsuite/g++.dg/template/access32.C
new file mode 100644
index 0000000..08faa9f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access32.C
@@ -0,0 +1,8 @@
+// PR c++/98515
+// { dg-do compile }
+
+struct A { protected: int var0; };
+template <class> struct B : public A { };
+template <class T> struct C : public B<T> { void g(); };
+template <class T> void C<T>::g() { A::var0++; }
+template class C<int>;
diff --git a/gcc/testsuite/g++.dg/template/access33.C b/gcc/testsuite/g++.dg/template/access33.C
new file mode 100644
index 0000000..9fb9b9a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access33.C
@@ -0,0 +1,9 @@
+// PR c++/98515
+// { dg-do compile }
+
+struct A { protected: int var0; };
+template <class> struct B : public A { };
+template <class T> struct C : public B<T> { void g(); };
+template <class T> void C<T>::g() { A::var0++; } // { dg-error "protected|invalid" }
+template <> struct B<char> { };
+template class C<char>;