This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] c++/pr77585 bogus this error with generic lambda
- From: Nathan Sidwell <nathan at acm dot org>
- To: Jason Merrill <jason at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 15 Dec 2016 07:38:41 -0500
- Subject: [PATCH] c++/pr77585 bogus this error with generic lambda
- Authentication-results: sourceware.org; auth=none
77585 concerns the instantiation of a generic lambda that contains a
call to a non-dependent non-static member function.
auto lam = [&](auto) { return Share (); };
r += Eat (lam); // instantation of lambda::operator() here
During instantiation of the call to Share, maybe_resolve_dummy gets
called and uses current_nonlambda_class_type, which peeks up the
current_current_class stack.
That peeking presupposes we're actually pushing and popping class scopes
as we enter them all the way from the global scope. But that doesn't
always happen in instantiation. push_nested_class pushes the
immediately enclosing scopes, but stops at function scope. So we don't
get the class scope of that function pushed. Thus stack peeking fails.
This hasn't previously been an instantiation problem, because templates
couldn't be defined at local scope. But generic lambdas now have that
property (wrt this capture at least).
This patch amends instantiate_decl to first push the containing
non-lambda class scope before start_preparsed_function does its stack
pushing.
ok?
nathan
--
Nathan Sidwell
2016-12-14 Nathan Sidwell <nathan@acm.org>
PR c++/77585
* pt.c (instantiate_decl): Push to class scope lambda resides
within when instantiating a generic lambda function.
PR c++/77585
* g++.dg/cpp1y/pr77585.C: New.
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 243661)
+++ cp/pt.c (working copy)
@@ -22483,6 +22483,7 @@ instantiate_decl (tree d, int defer_ok,
tree tmpl_parm;
tree spec_parm;
tree block = NULL_TREE;
+ tree lambda_ctx = NULL_TREE;
/* Save away the current list, in case we are instantiating one
template from within the body of another. */
@@ -22496,7 +22497,23 @@ instantiate_decl (tree d, int defer_ok,
&& TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL)
block = push_stmt_list ();
else
- start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED);
+ {
+ if (LAMBDA_FUNCTION_P (d))
+ {
+ /* When instantiating a lambda's templated function
+ operator, we need to push the non-lambda class scope
+ of the lambda itself so that the nested function
+ stack is sufficiently correct to deal with this
+ capture. */
+ lambda_ctx = DECL_CONTEXT (d);
+ do
+ lambda_ctx = decl_type_context (TYPE_NAME (lambda_ctx));
+ while (lambda_ctx && LAMBDA_TYPE_P (lambda_ctx));
+ if (lambda_ctx)
+ push_nested_class (lambda_ctx);
+ }
+ start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED);
+ }
/* Some typedefs referenced from within the template code need to be
access checked at template instantiation time, i.e now. These
@@ -22564,6 +22581,8 @@ instantiate_decl (tree d, int defer_ok,
d = finish_function (0);
expand_or_defer_fn (d);
}
+ if (lambda_ctx)
+ pop_nested_class ();
if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
cp_check_omp_declare_reduction (d);
Index: testsuite/g++.dg/cpp1y/pr77585.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr77585.C (revision 0)
+++ testsuite/g++.dg/cpp1y/pr77585.C (working copy)
@@ -0,0 +1,41 @@
+// PR c++/77585
+// { dg-do run { target c++14 } }
+
+// Confusion about this capture when instantiating generic lambda's
+// function operator
+
+template <typename F> int Eat (F &&f) { return f (1); }
+
+struct Foo {
+ int x = 1;
+ int Share () { return x++; }
+ int Frob (int);
+};
+
+int Foo::Frob (int r)
+{
+ auto lam = [&](auto) { return Share (); };
+ r += Eat (lam);
+
+ auto lam0 = [&](auto) {
+ auto lam1 = [&](auto) { return Share (); };
+ return Eat (lam1); };
+ r += Eat (lam0);
+
+ return r;
+}
+
+int Frob (int r)
+{
+ auto lam = [&](auto) { return 1; };
+ r += Eat (lam);
+ return r;
+}
+
+
+int main ()
+{
+ Foo f;
+
+ return Frob (f.Frob (0)) == 4 ? 0 : 1;
+}