On Fri, 25 Jun 2021, Jason Merrill wrote: > On 6/25/21 11:03 AM, 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 > > 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); > > How about replacing these two calls with push_nested_class (type), like we use > later in the function?
That works nicely. Would the patch be OK with that change? Bootstrapped and regtested on x86_64-pc-linux-gnu. > > > t = most_specialized_partial_spec (type, tf_warning_or_error); > > + popclass (); > > + pop_access_scope (TYPE_NAME (type)); > > 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; > > + } > > +}; > > > >