On Fri, 25 Jun 2021, Patrick Palka wrote: > Here, when determining whether the partial specialization matches the > specialization has_set_attr_method<Child>, we do so from the scope of
Er, this should say has_type_method<Child>. > where the template-id appears rather than from the scope of the > specialization, and this causes us to select the partial specialization > (since Child::type is accessible from Parent). When we later > instantiate this partial specialization, we've entered the scope of the > specialization and so substitution into e.g. the DECL_CONTEXT for > 'value' yields access errors for Child::type since the friend > declaration no longer applies. > > It seems the appropriate access scope from which to perform partial > specialization matching is the specialization itself (similar to how > we check access of base-clauses), which is what this patch implements. > > There's implementation divergence however: Clang accepts both testcases > below whereas MSVC and ICC reject both (indicating that Clang performs > partial spec matching from the scope of the specialization and MSVC/ICC > performs it from whatever scope the template-id appears). > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk? > > PR c++/96204 > > gcc/cp/ChangeLog: > > * pt.c (instantiate_class_template_1): Enter the scope of the > type before calling most_specialized_partial_spec. > > gcc/testsuite/ChangeLog: > > * g++.dg/template/access40.C: New test. > * g++.dg/template/access40a.C: New test. > --- > gcc/cp/pt.c | 6 ++++- > gcc/testsuite/g++.dg/template/access40.C | 30 +++++++++++++++++++++++ > gcc/testsuite/g++.dg/template/access40a.C | 30 +++++++++++++++++++++++ > 3 files changed, 65 insertions(+), 1 deletion(-) > create mode 100644 gcc/testsuite/g++.dg/template/access40.C > create mode 100644 gcc/testsuite/g++.dg/template/access40a.C > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index f4e0abe5c1e..5107bfbf9d1 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -11774,8 +11774,12 @@ instantiate_class_template_1 (tree type) > deferring_access_check_sentinel acs (dk_no_deferred); > > /* Determine what specialization of the original template to > - instantiate. */ > + instantiate; do this relative to the scope of the type. */ > + push_access_scope (TYPE_NAME (type)); > + pushclass (type); D'oh, this pair of calls is equivalent to push_nested_class; consider this hunk replaced by a single call to push_nested_class(type). > t = most_specialized_partial_spec (type, tf_warning_or_error); > + popclass (); > + pop_access_scope (TYPE_NAME (type)); Likewise consider this replaced with pop_nested_class(). > if (t == error_mark_node) > return error_mark_node; > else if (t) > diff --git a/gcc/testsuite/g++.dg/template/access40.C > b/gcc/testsuite/g++.dg/template/access40.C > new file mode 100644 > index 00000000000..e0d30779377 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/access40.C > @@ -0,0 +1,30 @@ > +// PR c++/96204 > + > +template<bool> struct bool_constant; > + > +template<class, class = void> > +struct has_type_member { > + static const bool value = false; > +}; > + > +template<class T> > +struct has_type_member<T, typename T::type> { > + static const bool value = true; > +}; > + > +struct Parent; > + > +struct Child { > +private: > + friend struct Parent; > + typedef void type; > +}; > + > +struct Parent { > + static void f() { > + // The partial specialization of has_type_member does not match > + // despite Child::type being accessible from the current scope. > + typedef bool_constant<has_type_member<Child>::value> type; > + typedef bool_constant<false> type; > + } > +}; > diff --git a/gcc/testsuite/g++.dg/template/access40a.C > b/gcc/testsuite/g++.dg/template/access40a.C > new file mode 100644 > index 00000000000..85138c9e570 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/access40a.C > @@ -0,0 +1,30 @@ > +// PR c++/96204 > + > +template<bool> struct bool_constant; > + > +template<class, class = void> > +struct has_type_member { > + static const bool value = false; > +}; > + > +template<class T> > +struct has_type_member<T, typename T::type> { > + static const bool value = true; > +}; > + > +struct Parent; > + > +struct Child { > +private: > + friend struct has_type_member<Child>; > + typedef void type; > +}; > + > +struct Parent { > + static void f() { > + // The partial specialization matches because Child::type is > + // accessible from has_type_member<Child>. > + typedef bool_constant<has_type_member<Child>::value> type; > + typedef bool_constant<true> type; > + } > +}; > -- > 2.32.0.93.g670b81a890 > >