diff mbox

C++ PATCH for c++/53137 (ICE with lambda in template)

Message ID 4FC8F2EA.4010001@redhat.com
State New
Headers show

Commit Message

Jason Merrill June 1, 2012, 4:50 p.m. UTC
In 4.7, this is one bug: we are doing a push_to_top_level to instantiate 
the lambda, and then not pushing back into the function scope that the 
lambda belongs to.  Fixed by following the lead of synthesize_method and 
only doing push_function_context if we're dealing with a local class.

On the trunk, after that issue was fixed I also had to fix our handling 
of 'this' capture in template lambdas after my work on return type 
deduction for arbitrary functions.

Tested x86_64-pc-linux-gnu, applying to trunk and 4.7.

Comments

H.J. Lu June 30, 2012, 7:22 p.m. UTC | #1
On Fri, Jun 1, 2012 at 9:50 AM, Jason Merrill <jason@redhat.com> wrote:
> In 4.7, this is one bug: we are doing a push_to_top_level to instantiate the
> lambda, and then not pushing back into the function scope that the lambda
> belongs to.  Fixed by following the lead of synthesize_method and only doing
> push_function_context if we're dealing with a local class.
>
> On the trunk, after that issue was fixed I also had to fix our handling of
> 'this' capture in template lambdas after my work on return type deduction
> for arbitrary functions.
>
> Tested x86_64-pc-linux-gnu, applying to trunk and 4.7.

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53821
diff mbox

Patch

commit 1409620970858e91fc55f90935e724b511f98d6c
Author: Jason Merrill <jason@redhat.com>
Date:   Thu May 31 17:25:52 2012 -0400

    	PR c++/53137
    	* pt.c (instantiate_class_template_1): Set LAMBDA_EXPR_THIS_CAPTURE.
    	(instantiate_decl): Don't push_to_top_level for local class methods.
    	(instantiate_class_template_1): Or for local classes.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b58dd13..4d4e8ad 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8698,6 +8698,7 @@  instantiate_class_template_1 (tree type)
   tree pbinfo;
   tree base_list;
   unsigned int saved_maximum_field_alignment;
+  tree fn_context;
 
   if (type == error_mark_node)
     return error_mark_node;
@@ -8756,7 +8757,9 @@  instantiate_class_template_1 (tree type)
      it now.  */
   push_deferring_access_checks (dk_no_deferred);
 
-  push_to_top_level ();
+  fn_context = decl_function_context (TYPE_MAIN_DECL (type));
+  if (!fn_context)
+    push_to_top_level ();
   /* Use #pragma pack from the template context.  */
   saved_maximum_field_alignment = maximum_field_alignment;
   maximum_field_alignment = TYPE_PRECISION (pattern);
@@ -9154,8 +9157,14 @@  instantiate_class_template_1 (tree type)
       tree decl = lambda_function (type);
       if (decl)
 	{
+	  tree lam = CLASSTYPE_LAMBDA_EXPR (type);
+	  LAMBDA_EXPR_THIS_CAPTURE (lam)
+	    = lookup_field_1 (type, get_identifier ("__this"), false);
+
 	  instantiate_decl (decl, false, false);
 	  maybe_add_lambda_conv_op (type);
+
+	  LAMBDA_EXPR_THIS_CAPTURE (lam) = NULL_TREE;
 	}
       else
 	gcc_assert (errorcount);
@@ -9186,7 +9195,8 @@  instantiate_class_template_1 (tree type)
   perform_deferred_access_checks ();
   pop_nested_class ();
   maximum_field_alignment = saved_maximum_field_alignment;
-  pop_from_top_level ();
+  if (!fn_context)
+    pop_from_top_level ();
   pop_deferring_access_checks ();
   pop_tinst_level ();
 
@@ -18435,9 +18445,10 @@  instantiate_decl (tree d, int defer_ok,
   tree spec;
   tree gen_tmpl;
   bool pattern_defined;
-  int need_push;
   location_t saved_loc = input_location;
   bool external_p;
+  tree fn_context;
+  bool nested;
 
   /* This function should only be used to instantiate templates for
      functions and static member variables.  */
@@ -18672,9 +18683,12 @@  instantiate_decl (tree d, int defer_ok,
 	goto out;
     }
 
-  need_push = !cfun || !global_bindings_p ();
-  if (need_push)
+  fn_context = decl_function_context (d);
+  nested = (current_function_decl != NULL_TREE);
+  if (!fn_context)
     push_to_top_level ();
+  else if (nested)
+    push_function_context ();
 
   /* Mark D as instantiated so that recursive calls to
      instantiate_decl do not try to instantiate it again.  */
@@ -18784,8 +18798,10 @@  instantiate_decl (tree d, int defer_ok,
   /* We're not deferring instantiation any more.  */
   TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (d)) = 0;
 
-  if (need_push)
+  if (!fn_context)
     pop_from_top_level ();
+  else if (nested)
+    pop_function_context ();
 
 out:
   input_location = saved_loc;
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-template5.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-template5.C
new file mode 100644
index 0000000..b91b89f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-template5.C
@@ -0,0 +1,17 @@ 
+// PR c++/53137
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  template <typename T> void f();
+
+  template <typename T> void g()
+  {
+    [this]{ f<T>(); }();
+  }
+
+  void h()
+  {
+    g<int>();
+  }
+};