On 1/22/21 1:58 PM, Patrick Palka wrote:
On Fri, 22 Jan 2021, Jason Merrill wrote:
On 1/22/21 12:59 PM, Patrick Palka wrote:
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.
What if we flipped the sense of the type_dependent_expression_p check, so we
never add this-> if the function operand is dependent?
Looks like this doesn't survive 'make check-c++', we crash on the
testcase g++.dg/cpp1y/lambda-generic-this1a.C:
gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1a.C: In instantiation of
‘MyType::crash()::<lambda(auto:1)> [with auto:1 = A]’:
gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1a.C:12:10: required from here
gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1a.C:9:28: internal compiler
error: trying to capture ‘this’ in instantiation of generic lambda
9 | MyType::make_crash<i>(); // Line (1)
| ~~~~~~~~~~~~~~~~~~~~~^~
0x9bab23 add_capture(tree_node*, tree_node*, tree_node*, bool, bool)
/home/patrick/gcc-master/gcc/cp/lambda.c:633
0x9bac75 add_default_capture(tree_node*, tree_node*, tree_node*)
/home/patrick/gcc-master/gcc/cp/lambda.c:693
0x9bb3a1 lambda_expr_this_capture(tree_node*, int)
/home/patrick/gcc-master/gcc/cp/lambda.c:811
0x9bb4a0 maybe_resolve_dummy(tree_node*, bool)
/home/patrick/gcc-master/gcc/cp/lambda.c:894
0x8de510 build_new_method_call_1
/home/patrick/gcc-master/gcc/cp/call.c:10503
0x8df04f build_new_method_call(tree_node*, tree_node*, vec<tree_node*, va_gc,
vl_embed>**, tree_node*, int, tree_node**, int)
Presumably because we no longer add 'this->' to the dependent call
MyType::make_crash<i> inside the generic lambda, so we don't capture it
when we should have?
Ah, yes, we still need to add this-> in a generic lambda, or at least
capture 'this'.
I think I was wrong in my assertion around Alex's patch that
shared_member_p should abort on a dependent USING_DECL; it now seems
appropriate for it to return false if we don't know, we just need to
adjust the comment to say that.
And then drop the type-dependent check.
To be clear, this is with
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2214,8 +2214,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 ((type_dependent_expression_p (expr)
- || !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 ()))
-- >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.
I don't see this change in the patch; it seems like a good cleanup even if it
isn't needed for this bug.
Sounds good. I'll add it back in (and the testcase cpp0x/this2.C) in the next
iteration of the patch.
(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