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)
+              && 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