[PATCH] c++: Always check access during late-parsing of members [PR58993]

Patrick Palka ppalka@redhat.com
Tue Jan 12 22:26:50 GMT 2021


This patch removes a vestigial use of dk_no_check from
cp_parser_late_parsing_for_member, which ideally should have been
removed as part of the PR41437 patch that improved access checking
inside templates.  This allows us to correctly reject f1 and f2 below in
the testcase access34.C below (whereas before we'd only reject f3).

Additional testing revealed an access issue when late-parsing a hidden
friend within a class template.  In the testcase friend68.C below, we're
tripping over the checking assert from friend_accessible_p(f, S::j, S, S)
during lookup of j in x.j (for which type_dependent_object_expression_p
returns false, which is why we're doing the lookup at parse time).  The
reason for the assert failure is that DECL_FRIENDLIST(S) contains f but
DECL_BEFRIENDING_CLASSES(f) is empty, and so friend_accessible_p (which
looks at DECL_BEFRIENDING_CLASSES) wants to return false, but is_friend
(which looks at DECL_FRIENDLIST) returns true.  For sake of symmetry one
would probably hope that DECL_BEFRIENDING_CLASSES(f) contains S, but
add_friend avoids updating DECL_BEFRIENDING_CLASSES when the class type
(S in this case) is dependent, for some reason.

This patch works around this issue by making friend_accessible_p
consider the DECL_FRIEND_CONTEXT of SCOPE.  Thus we sidestep the
DECL_BEFRIENDING_CLASSES / DECL_FRIENDLIST asymmetry issue while
correctly validating the x.j access at parse time.

A earlier version of this patch called friend_accessible_p instead of
protected_accessible_p in the DECL_FRIEND_CONTEXT hunk below, but this
had the side effect of making us accept the ill-formed testcase
friend69.C below (ill-formed because a hidden friend is not actually a
class member, so g doesn't have access to B's members even though A is a
friend of B).

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look like
the right approach?

gcc/cp/ChangeLog:

	PR c++/41437
	PR c++/58993
	* search.c (friend_accessible_p): If scope is a hidden friend
	defined inside a dependent class, consider access from the
	defining class.
	* parser.c (cp_parser_late_parsing_for_member): Don't push a
	dk_no_check access state.

gcc/testsuite/ChangeLog:

	PR c++/41437
	PR c++/58993
	* g++.dg/opt/pr87974.C: Adjust.
	* g++.dg/template/access34.C: New test.
	* g++.dg/template/friend68a.C: New test.
	* g++.dg/template/friend68b.C: New test.
	* g++.dg/template/friend69.C: New test.
---
 gcc/cp/parser.c                          |  7 ------
 gcc/cp/search.c                          |  8 +++++++
 gcc/testsuite/g++.dg/opt/pr87974.C       |  1 +
 gcc/testsuite/g++.dg/template/access34.C | 29 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/friend68.C | 13 +++++++++++
 gcc/testsuite/g++.dg/template/friend69.C | 18 +++++++++++++++
 6 files changed, 69 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/access34.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend68.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend69.C

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index b95151bf90d..c26e16beb70 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -30825,10 +30825,6 @@ cp_parser_late_parsing_for_member (cp_parser* parser, tree member_function)
       start_preparsed_function (member_function, NULL_TREE,
 				SF_PRE_PARSED | SF_INCLASS_INLINE);
 
-      /* Don't do access checking if it is a templated function.  */
-      if (processing_template_decl)
-	push_deferring_access_checks (dk_no_check);
-
       /* #pragma omp declare reduction needs special parsing.  */
       if (DECL_OMP_DECLARE_REDUCTION_P (member_function))
 	{
@@ -30842,9 +30838,6 @@ cp_parser_late_parsing_for_member (cp_parser* parser, tree member_function)
 	cp_parser_function_definition_after_declarator (parser,
 							/*inline_p=*/true);
 
-      if (processing_template_decl)
-	pop_deferring_access_checks ();
-
       /* Leave the scope of the containing function.  */
       if (function_scope)
 	pop_function_context ();
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index 1a9dba451c7..dd3773da4f7 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -698,6 +698,14 @@ friend_accessible_p (tree scope, tree decl, tree type, tree otype)
       if (DECL_CLASS_SCOPE_P (scope)
 	  && friend_accessible_p (DECL_CONTEXT (scope), decl, type, otype))
 	return 1;
+      /* Perhaps SCOPE is a friend function defined inside a class from which
+	 DECL is accessible.  Checking this is necessary only when the class
+	 is dependent, for otherwise add_friend will already have added the
+	 class to SCOPE's DECL_BEFRIENDING_CLASSES.  */
+      if (tree fctx = DECL_FRIEND_CONTEXT (scope))
+	if (dependent_type_p (fctx)
+	    && protected_accessible_p (decl, fctx, type, otype))
+	  return 1;
     }
 
   /* Maybe scope's template is a friend.  */
diff --git a/gcc/testsuite/g++.dg/opt/pr87974.C b/gcc/testsuite/g++.dg/opt/pr87974.C
index 4b070d2a6f8..7d8357a025c 100644
--- a/gcc/testsuite/g++.dg/opt/pr87974.C
+++ b/gcc/testsuite/g++.dg/opt/pr87974.C
@@ -8,6 +8,7 @@ class i {
     struct j {
 	using c = int *;
     };
+public:
     using as = j::c;
 };
 template <typename> class k {
diff --git a/gcc/testsuite/g++.dg/template/access34.C b/gcc/testsuite/g++.dg/template/access34.C
new file mode 100644
index 00000000000..ec79f87e624
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access34.C
@@ -0,0 +1,29 @@
+// PR c++/58993
+// { dg-do compile }
+
+class base { void foo(); };
+
+template <class T>
+struct bar : public base {
+  void f1() {
+    &base::foo;  // { dg-error "private" }
+  }
+
+  template <class>
+  void f2() {
+    &base::foo;  // { dg-error "private" }
+  }
+
+  void f3();
+};
+
+template <class T>
+void bar<T>::f3() {
+  (void) &base::foo; // { dg-error "private" }
+}
+
+int main() {
+  bar<int>().f1();
+  bar<int>().f2<int>();
+  bar<int>().f3();
+}
diff --git a/gcc/testsuite/g++.dg/template/friend68.C b/gcc/testsuite/g++.dg/template/friend68.C
new file mode 100644
index 00000000000..fa34d2c30d5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend68.C
@@ -0,0 +1,13 @@
+// { dg-do compile }
+
+template <class>
+struct S {
+  S();
+  friend int f(S x) { return x.i + x.j; }
+  template <class T>
+    friend int g(S x, T) { return x.i + x.j; }
+private:
+  int i;
+protected:
+  int j;
+};
diff --git a/gcc/testsuite/g++.dg/template/friend69.C b/gcc/testsuite/g++.dg/template/friend69.C
new file mode 100644
index 00000000000..f3086a9f980
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend69.C
@@ -0,0 +1,18 @@
+// { dg-do compile }
+
+struct A;
+
+struct B {
+  friend struct A;
+private:
+  static void f();
+protected:
+  static void g();
+};
+
+struct A {
+  friend void g(A) {
+    B::f(); // { dg-error "private" }
+    B::g(); // { dg-error "protected" }
+  }
+};
-- 
2.30.0



More information about the Gcc-patches mailing list