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
> 
> 

Reply via email to