c++: access for hidden friend of nested class template [PR100502]
Here, during ahead of time access checking for the private member EnumeratorRange<T>::end_reached_ in the hidden friend f, we're triggering the assert in enforce_access that verifies we're not trying to add a access check for a dependent decl onto TI_DEFERRED_ACCESS_CHECKS. The special thing about this class member access expression is that the overall expression is non-dependent (so finish_class_member_access_expr doesn't exit early at parse time), and then accessible_p rejects the access (so we don't exit early from enforce access either, and end up triggering the assert b/c the member itself is dependent). I think we're correct to reject the access because a hidden friend is not a member function, so [class.access.nest] doesn't apply, and also a hidden friend of a nested class is not a friend of the enclosing class. To fix this ICE, this patch disables ahead of time access checking during the member lookup in finish_class_member_access_expr. This avoids potentially pushing an access check for a dependent member onto TI_DEFERRED_ACCESS_CHECKS, and it's safe because we're going to redo the same lookup at instantiation time anyway. PR c++/100502 gcc/cp/ChangeLog: * typeck.c (finish_class_member_access_expr): Disable ahead of time access checking during the member lookup. gcc/testsuite/ChangeLog: * g++.dg/template/access37.C: New test. * g++.dg/template/access37a.C: New test.
This commit is contained in:
parent
a42220f016
commit
abe8787a84
3 changed files with 42 additions and 0 deletions
|
@ -3201,9 +3201,19 @@ finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
|
|||
{
|
||||
/* Look up the member. */
|
||||
access_failure_info afi;
|
||||
if (processing_template_decl)
|
||||
/* Even though this class member access expression is at this
|
||||
point not dependent, the member itself may be dependent, and
|
||||
we must not potentially push a access check for a dependent
|
||||
member onto TI_DEFERRED_ACCESS_CHECKS. So don't check access
|
||||
ahead of time here; we're going to redo this member lookup at
|
||||
instantiation time anyway. */
|
||||
push_deferring_access_checks (dk_no_check);
|
||||
member = lookup_member (access_path, name, /*protect=*/1,
|
||||
/*want_type=*/false, complain,
|
||||
&afi);
|
||||
if (processing_template_decl)
|
||||
pop_deferring_access_checks ();
|
||||
afi.maybe_suggest_accessor (TYPE_READONLY (object_type));
|
||||
if (member == NULL_TREE)
|
||||
{
|
||||
|
|
26
gcc/testsuite/g++.dg/template/access37.C
Normal file
26
gcc/testsuite/g++.dg/template/access37.C
Normal file
|
@ -0,0 +1,26 @@
|
|||
// PR c++/100502
|
||||
|
||||
template <class T>
|
||||
struct EnumeratorRange {
|
||||
struct Iterator {
|
||||
EnumeratorRange range_;
|
||||
|
||||
friend void f(Iterator i) {
|
||||
i.range_.end_reached_; // { dg-error "private" }
|
||||
i.range_.EnumeratorRange::end_reached_; // { dg-error "private" }
|
||||
&i.range_.end_reached_; // { dg-error "private" }
|
||||
&i.range_.EnumeratorRange::end_reached_; // { dg-error "private" }
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool end_reached_;
|
||||
#if DECLARE_FRIEND
|
||||
friend void f(Iterator);
|
||||
#endif
|
||||
};
|
||||
|
||||
int main() {
|
||||
EnumeratorRange<int>::Iterator i;
|
||||
f(i);
|
||||
}
|
6
gcc/testsuite/g++.dg/template/access37a.C
Normal file
6
gcc/testsuite/g++.dg/template/access37a.C
Normal file
|
@ -0,0 +1,6 @@
|
|||
// PR c++/100502
|
||||
// { dg-additional-options "-DDECLARE_FRIEND -Wno-non-template-friend" }
|
||||
|
||||
// Verify that access37.C is accepted if the appropriate friend relation
|
||||
// is declared (controlled by the macro DECLARE_FRIEND).
|
||||
#include "access37.C"
|
Loading…
Add table
Reference in a new issue