On Fri, 22 Jan 2021, Patrick Palka wrote:

> On Thu, 21 Jan 2021, Jason Merrill wrote:
> 
> > On 1/21/21 11:22 AM, Patrick Palka wrote:
> > > Here at parse time finish_qualified_id_expr adds an implicit 'this->' to
> > > the expression tmp::integral<T> (because it's type-dependent, and also
> > > current_class_ptr is set) within the trailing return type, and later
> > > during substitution we can't resolve the 'this' since
> > > tsubst_function_type does inject_this_parm only for non-static member
> > > functions which tmp::func is not.
> > > 
> > > It seems the root of the problem is that we set current_class_ptr when
> > > parsing the signature of a static member function.  Since explicit uses
> > > of 'this' are already not allowed in this context, we probably shouldn't
> > > be doing inject_this_parm either.
> > 
> > Hmm, 'this' is defined in a static member function, it's just ill-formed to
> > use it:
> > 
> > 7.5.2/2: "... [this] shall not appear within the declaration of a static
> > member function (although its type and value category are defined within a
> > static member function as they are within a non-static member function).
> > [Note: This is because declaration matching does not occur until the 
> > complete
> > declarator is known. — end note]"
> 
> Ah, I see.
> 
> > 
> > Perhaps maybe_dummy_object needs to be smarter about recognizing static
> > context.  Or perhaps there's no way to tell the difference between the
> > specified behavior above and the behavior with your patch, but:
> > 
> > The note suggests that we need to test the case of an out-of-class 
> > definition
> > of a static member function (template); we must inject_this_parm when 
> > parsing
> > such a declaration, since we don't know it's static until we match it to the
> > declaration in the class.  I'm guessing that this would lead to the same
> > problem.
> 
> Good point, we do get a signature mismatch error in this case, due to
> 'this->' appearing in the trailing return type out-of-class declaration
> but not in that of the in-class declaration, so the trailing return
> types don't match up.  In light of this case, it doesn't seem possible
> for maybe_dummy_object to distinguish between a static and non-static
> context when parsing the trailing return type of the declaration.
> 
> So perhaps we should mirror at substitution time what we do at parse
> time, and inject 'this' also when substituting into the function type of
> a static member function?  That way, we'll resolve the use of 'this->'
> and then discard it the RHS of -> is a static member function, or
> complain that 'this' is not a constant expression if the RHS is a
> non-static member function.
> 
> The following patch performs that, and seems to pass light testing.
> Full testing in progress.  Revised commit message is still a WIP.
> How does this approach look?
> 
> -- >8 --
> 
> Subject: [PATCH] c++: 'this' injection and static member functions [PR97399]
> 
> gcc/cp/ChangeLog:
> 
>       PR c++/97399
>       * parser.c (cp_parser_init_declarator): If the storage class
>       specifier is sc_static, pass true for static_p to
>       cp_parser_declarator.
>       (cp_parser_direct_declarator): Don't do inject_this_parm when
>       the function is specified as a friend.
>       * pt.c (tsubst_function_type): Also inject 'this' when
>       substituting into the function type of a static member
>       function.
> 
> gcc/testsuite/ChangeLog:
> 
>       PR c++/88548
>       PR c++/97399
>       * g++.dg/cpp0x/this2.C: New test.
>       * g++.dg/gomp/this-1.C: Adjust expected error for use of 'this'
>       in signature of static member function.
>       * g++.dg/template/pr97399a.C: New test.
>       * g++.dg/template/pr97399b.C: New test.
> ---
>  gcc/cp/parser.c                          |  2 +-
>  gcc/cp/pt.c                              | 13 +++++++++++--
>  gcc/testsuite/g++.dg/template/pr97399a.C | 23 +++++++++++++++++++++++
>  gcc/testsuite/g++.dg/template/pr97399b.C | 23 +++++++++++++++++++++++
>  4 files changed, 58 insertions(+), 3 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/template/pr97399a.C
>  create mode 100644 gcc/testsuite/g++.dg/template/pr97399b.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 48437f23175..a0efa55d21e 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -22122,7 +22122,7 @@ cp_parser_direct_declarator (cp_parser* parser,
>  
>                 tree save_ccp = current_class_ptr;
>                 tree save_ccr = current_class_ref;
> -               if (memfn)
> +               if (memfn && !friend_p)
>                   /* DR 1207: 'this' is in scope after the cv-quals.  */
>                   inject_this_parameter (current_class_type, cv_quals);
>  
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index ad855dbde72..d4763325e25 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -15072,8 +15072,17 @@ tsubst_function_type (tree t,
>  
>        tree save_ccp = current_class_ptr;
>        tree save_ccr = current_class_ref;
> -      tree this_type = (TREE_CODE (t) == METHOD_TYPE
> -                     ? TREE_TYPE (TREE_VALUE (arg_types)) : NULL_TREE);
> +      tree this_type = NULL_TREE;
> +      if (TREE_CODE (t) == METHOD_TYPE)
> +     this_type = TREE_TYPE (TREE_VALUE (arg_types));
> +      else if (in_decl
> +            && DECL_FUNCTION_MEMBER_P (in_decl)

Oops, this line should instead be checking DECL_STATIC_FUNCTION_P.
Consider it changed.

> +            && t == TREE_TYPE (in_decl))
> +     /* Also inject 'this' when substituting into the function type
> +        of a static member function, as we may have conservatively
> +        added 'this->' to a dependent member function call in its
> +        trailing return type which we might need to resolve.  */
> +     this_type = DECL_CONTEXT (in_decl);
>        bool do_inject = this_type && CLASS_TYPE_P (this_type);
>        if (do_inject)
>       {
> diff --git a/gcc/testsuite/g++.dg/template/pr97399a.C 
> b/gcc/testsuite/g++.dg/template/pr97399a.C
> new file mode 100644
> index 00000000000..4bb818908fd
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/pr97399a.C
> @@ -0,0 +1,23 @@
> +// PR c++/97399
> +// { dg-do compile { target c++11 } }
> +
> +template <bool> struct enable_if_t {};
> +
> +struct tmp {
> +  template <class> static constexpr bool is_integral();
> +  template <class T> static auto f()
> +    -> enable_if_t<tmp::is_integral<T>()>;
> +  template <class T> friend auto g(tmp, T)
> +    -> enable_if_t<!tmp::is_integral<T>()>;
> +};
> +
> +template <class> constexpr bool tmp::is_integral() { return true; }
> +
> +template <class T> auto tmp::f()
> +  -> enable_if_t<tmp::is_integral<T>()> { return {}; }
> +
> +int main()
> +{
> +  tmp::f<int>();
> +  g(tmp{}, 0);
> +}
> diff --git a/gcc/testsuite/g++.dg/template/pr97399b.C 
> b/gcc/testsuite/g++.dg/template/pr97399b.C
> new file mode 100644
> index 00000000000..673ba6624e3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/pr97399b.C
> @@ -0,0 +1,23 @@
> +// PR c++/97399
> +// { dg-do compile { target c++11 } }
> +
> +template <bool> struct enable_if_t {};
> +
> +struct tmp {
> +  template <class> constexpr bool is_integral(); // non-static
> +  template <class T> static auto f()
> +    -> enable_if_t<tmp::is_integral<T>()>; // { dg-message "in template 
> argument" }
> +  template <class T> friend auto g(tmp, T)
> +    -> enable_if_t<!tmp::is_integral<T>()>; // { dg-error "without object" }
> +};
> +
> +template <class> constexpr bool tmp::is_integral() { return true; }
> +
> +template <class T> auto tmp::f() // { dg-error "'this' is not a constant" }
> +  -> enable_if_t<tmp::is_integral<T>()> { return {}; }
> +
> +int main()
> +{
> +  tmp::f<int>(); // { dg-error "no match" }
> +  g(tmp{}, 0); // { dg-error "no match" }
> +}
> -- 
> 2.30.0.155.g66e871b664
> 

Reply via email to