[PATCH] c++: access scope during partial spec matching [PR96204]

Jason Merrill jason@redhat.com
Fri Jun 25 20:25:44 GMT 2021


On 6/25/21 1:14 PM, Patrick Palka wrote:
> 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.

OK with that and the matching change...

>>>      t = most_specialized_partial_spec (type, tf_warning_or_error);
>>> +  popclass ();
>>> +  pop_access_scope (TYPE_NAME (type));

...here.

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



More information about the Gcc-patches mailing list