On Fri, 22 Jan 2021, Patrick Palka wrote:

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

Sorry for the spam, but I'd also like to propose a more conservative and
targeted approach that basically undoes part of the r9-5972 patch which
caused the regression.

According to the email thread at
https://gcc.gnu.org/pipermail/gcc-patches/2019-February/516421.html
the hunk from r9-5972

--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2096,7 +2096,8 @@ finish_qualified_id_expr (tree qualifying_class,
     {
       /* See if any of the functions are non-static members.  */
       /* If so, the expression may be relative to 'this'.  */
-      if (!shared_member_p (expr)
+      if ((type_dependent_expression_p (expr)
+          || !shared_member_p (expr))
          && current_class_ptr
          && DERIVED_FROM_P (qualifying_class,
                             current_nonlambda_class_type ()))

was added to avoid calling shared_member_p here when the overload set
contains a dependent USING_DECL.  But according to
https://gcc.gnu.org/pipermail/gcc-patches/2018-December/513237.html if
an overload set contains a dependent USING_DECL, then it'll be the first
in the overload set.  So we could partially revert the r9-5972 patch and
do:

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index c6b4c70dc0f..6b0d68ae4bf 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2214,7 +2214,7 @@ finish_qualified_id_expr (tree qualifying_class,
     {
       /* See if any of the functions are non-static members.  */
       /* If so, the expression may be relative to 'this'.  */
-      if ((type_dependent_expression_p (expr)
+      if ((TREE_CODE (get_first_fn (expr)) == USING_DECL
           || !shared_member_p (expr))
          && current_class_ptr
          && DERIVED_FROM_P (qualifying_class,

The above pases 'make check-c++', and allows us to compile pr97399a.C
successfully, but we'd still ICE on the invalid pr97399b.C.

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