aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2021-01-20 09:43:48 -0500
committerPatrick Palka <ppalka@redhat.com>2021-01-20 09:43:48 -0500
commit79e1251b642db038df276153c9f2ec6b82e56162 (patch)
treeec8ef4360954f0164fe7800a45e3b85d96079c81
parentea74a3f548eb321429c371e327e778e63d9128a0 (diff)
downloadgcc-79e1251b642db038df276153c9f2ec6b82e56162.zip
gcc-79e1251b642db038df276153c9f2ec6b82e56162.tar.gz
gcc-79e1251b642db038df276153c9f2ec6b82e56162.tar.bz2
c++: Defer access checking when processing bases [PR82613]
When parsing the base-clause of a class declaration, we need to defer access checking until the entire base-clause has been seen, so that access can be properly checked relative to the scope of the class with all its bases attached. This allows us to accept the declaration of struct D from Example 2 of [class.access.general] (access12.C below). Similarly when substituting into the base-clause of a class template, which is the subject of PR82613. gcc/cp/ChangeLog: PR c++/82613 * parser.c (cp_parser_class_head): Defer access checking when parsing the base-clause until all bases are seen and attached to the class type. * pt.c (instantiate_class_template): Likewise when substituting into dependent bases. gcc/testsuite/ChangeLog: PR c++/82613 * g++.dg/parse/access12.C: New test. * g++.dg/template/access35.C: New test.
-rw-r--r--gcc/cp/parser.c30
-rw-r--r--gcc/cp/pt.c16
-rw-r--r--gcc/testsuite/g++.dg/parse/access12.C24
-rw-r--r--gcc/testsuite/g++.dg/template/access35.C19
4 files changed, 68 insertions, 21 deletions
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index eeffc2e..4b2bca3 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -25576,19 +25576,11 @@ cp_parser_class_head (cp_parser* parser,
is valid. */
- /* Get the list of base-classes, if there is one. */
+ /* Get the list of base-classes, if there is one. Defer access checking
+ until the entire list has been seen, as per [class.access.general]. */
+ push_deferring_access_checks (dk_deferred);
if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
- {
- /* PR59482: enter the class scope so that base-specifiers are looked
- up correctly. */
- if (type)
- pushclass (type);
- bases = cp_parser_base_clause (parser);
- /* PR59482: get out of the previously pushed class scope so that the
- subsequent pops pop the right thing. */
- if (type)
- popclass ();
- }
+ bases = cp_parser_base_clause (parser);
else
bases = NULL_TREE;
@@ -25597,6 +25589,20 @@ cp_parser_class_head (cp_parser* parser,
if (type && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
xref_basetypes (type, bases);
+ /* Now that all bases have been seen and attached to the class, check
+ accessibility of the types named in the base-clause. This must be
+ done relative to the class scope, so that we accept e.g.
+
+ struct A { protected: struct B {}; };
+ struct C : A::B, A {}; // OK: A::B is accessible via base A
+
+ as per [class.access.general]. */
+ if (type)
+ pushclass (type);
+ pop_to_parent_deferring_access_checks ();
+ if (type)
+ popclass ();
+
done:
/* Leave the scope given by the nested-name-specifier. We will
enter the class scope itself while processing the members. */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c9f5811..bcf4c6e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -11825,17 +11825,14 @@ instantiate_class_template_1 (tree type)
|| COMPLETE_OR_OPEN_TYPE_P (TYPE_CONTEXT (type)));
base_list = NULL_TREE;
+ /* Defer access checking while we substitute into the types named in
+ the base-clause. */
+ push_deferring_access_checks (dk_deferred);
if (BINFO_N_BASE_BINFOS (pbinfo))
{
tree pbase_binfo;
- tree pushed_scope;
int i;
- /* We must enter the scope containing the type, as that is where
- the accessibility of types named in dependent bases are
- looked up from. */
- pushed_scope = push_scope (CP_TYPE_CONTEXT (type));
-
/* Substitute into each of the bases to determine the actual
basetypes. */
for (i = 0; BINFO_BASE_ITERATE (pbinfo, i, pbase_binfo); i++)
@@ -11877,9 +11874,6 @@ instantiate_class_template_1 (tree type)
/* The list is now in reverse order; correct that. */
base_list = nreverse (base_list);
-
- if (pushed_scope)
- pop_scope (pushed_scope);
}
/* Now call xref_basetypes to set up all the base-class
information. */
@@ -11897,6 +11891,10 @@ instantiate_class_template_1 (tree type)
class, except we also need to push the enclosing classes. */
push_nested_class (type);
+ /* Now check accessibility of the types named in its base-clause,
+ relative to the scope of the class. */
+ pop_to_parent_deferring_access_checks ();
+
/* Now members are processed in the order of declaration. */
for (member = CLASSTYPE_DECL_LIST (pattern);
member; member = TREE_CHAIN (member))
diff --git a/gcc/testsuite/g++.dg/parse/access12.C b/gcc/testsuite/g++.dg/parse/access12.C
new file mode 100644
index 0000000..6963e0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/access12.C
@@ -0,0 +1,24 @@
+// Example 2 of [class.access.general]
+// { dg-do compile }
+
+class A {
+ typedef int I; // private member
+ I f();
+ friend I g(I);
+ static I x;
+ template<int> struct Q;
+ template<int> friend struct R;
+protected:
+ struct B { };
+};
+
+A::I A::f() { return 0; }
+A::I g(A::I p = A::x);
+A::I g(A::I p) { return 0; }
+A::I A::x = 0;
+// FIXME: We reject these two declarations because access checking of A::I
+// is not performed in the scope of the class being declared.
+// template<A::I> struct A::Q { };
+// template<A::I> struct R { };
+
+struct D: A::B, A { };
diff --git a/gcc/testsuite/g++.dg/template/access35.C b/gcc/testsuite/g++.dg/template/access35.C
new file mode 100644
index 0000000..70197eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access35.C
@@ -0,0 +1,19 @@
+// PR c++/82613
+// { dg-do compile }
+
+template <typename T> class B;
+
+class A {
+ friend class B<A>;
+ class Type {};
+};
+
+template <typename T>
+class B : T::Type { protected: class Type {}; };
+
+B<A> b;
+
+template <typename T>
+class C : B<T>::Type, B<T> {};
+
+C<A> c;