c++: Always check access during late-parsing of members [PR58993]
authorPatrick Palka <ppalka@redhat.com>
Tue, 19 Jan 2021 21:20:00 +0000 (16:20 -0500)
committerPatrick Palka <ppalka@redhat.com>
Tue, 19 Jan 2021 21:20:00 +0000 (16:20 -0500)
commit29853c653245c37ed31b6abcc9799b534372e938
treef4de3310bfd24eb1c1bd06f2c6cee0f02c2ead35
parentc37f1d4081f5a19e39192d13e2a3acea13662e5a
c++: Always check access during late-parsing of members [PR58993]

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 in
the testcase access34.C below (whereas before we'd only reject f3).

Additional testing revealed a new 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 expect 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 the access 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 checked 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 the hidden friend g is not actually a member
of A, so g doesn't have access to B's members despite B befriending A).

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
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/friend68.C: New test.
* g++.dg/template/friend69.C: New test.
gcc/cp/parser.c
gcc/cp/search.c
gcc/testsuite/g++.dg/opt/pr87974.C
gcc/testsuite/g++.dg/template/access34.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/friend68.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/friend69.C [new file with mode: 0644]