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 >