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;
> > + }
> > +};
> >
>
>