Jason,
I happened to be working on 67273, noticed a problem with my 77585 fix, and coincidentally 79253 got filed, which this also fixes.

In 67253, Wshadow checking was getting confused when determining the return type of an instantiated lambda.

template <typename T> void Foo (T &&lambda) {
  int ARG = 2;
  lambda (1);
}

void Baz () {
  Foo ([] (auto &&ARG) {});
}

maybe_instantiate_decl gets called when building the 'lambda (1)' call during the instantiation of Foo (to determine its return type). It goes ahead and calls instantiate_decl. instantiate_decl decides not to push to top level at:

  fn_context = decl_function_context (d);
  ...
  if (!fn_context)
    push_to_top_level ();
  else
    ...

because 'decl_function_context' is true (it's 'Baz'), but the current function is 'Foo<closure_type>'. We end up in store_parms thinking we're pushing a parm 'ARG' that shadows the local var 'ARG'. That doesn't result in wrong code, but does give a spurious shadowing warning.

Again, this is an artifact of generic lambdas being template members of function-scope classes. Not a thing that exists elsewhere.

Unfortunately, there is a case where we do want to stay at the current level -- when we're instantiating the lambda body during instantiation of the containing template function. instantiate_decl must be told in which context it's being invoked.

the 77585 problem is:
          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.  */
We don't want to do that when we're not pushing to top level (I had thought it was just extra work, but 79253 shows it's a correctness problem).

As 'defer_ok' is still an int parm, I did toy with making it tri-valued, but went for adding an additional parm, and correcting defer_ok's type in the process. All callers outside of pt.c were already passing true or false.

ok?

nathan

--
Nathan Sidwell
2017-01-26  Nathan Sidwell  <nat...@acm.org>

	PR c++/67273
	PR c++/79253
	* cp-tree.h (instantiate_decl): Make defer_ok bool, add
	lambda_subst flag.
	* pt.c: Fix instantiate_decl calls to pass true/false not 0/1
	(instantiate_class_template_1): Inform instantiate_decl of
	lambda fn substing.
	(instantiate_decl): Push to top level for lambda fn instanitation,
	except when lambda_subst.

	PR c++/67273
	PR c++/79253
	* g++.dg/cpp1y/pr67273.C: New.
	* g++.dg/cpp1y/pr79253.C: New.

Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 244932)
+++ cp/cp-tree.h	(working copy)
@@ -6182,7 +6182,8 @@ extern void do_decl_instantiation		(tree
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
 extern void maybe_instantiate_noexcept		(tree);
-extern tree instantiate_decl			(tree, int, bool);
+extern tree instantiate_decl			(tree, bool, bool,
+						 bool = false);
 extern int comp_template_parms			(const_tree, const_tree);
 extern bool uses_parameter_packs                (tree);
 extern bool template_parameter_pack_p           (const_tree);
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 244932)
+++ cp/pt.c	(working copy)
@@ -10676,7 +10676,8 @@ instantiate_class_template_1 (tree type)
 	    {
 	      /* Set function_depth to avoid garbage collection.  */
 	      ++function_depth;
-	      instantiate_decl (decl, false, false);
+	      instantiate_decl (decl, /*defer_ok=*/false, false,
+				/*lambda_subst=*/true);
 	      --function_depth;
 	    }
 
@@ -16024,7 +16025,8 @@ tsubst_expr (tree t, tree args, tsubst_f
 	  complete_type (tmp);
 	  for (fn = TYPE_METHODS (tmp); fn; fn = DECL_CHAIN (fn))
 	    if (!DECL_ARTIFICIAL (fn))
-	      instantiate_decl (fn, /*defer_ok*/0, /*expl_inst_class*/false);
+	      instantiate_decl (fn, /*defer_ok=*/false,
+				/*expl_inst_class=*/false);
 	}
       break;
 
@@ -21941,7 +21943,7 @@ do_decl_instantiation (tree decl, tree s
   check_explicit_instantiation_namespace (result);
   mark_decl_instantiated (result, extern_p);
   if (! extern_p)
-    instantiate_decl (result, /*defer_ok=*/1,
+    instantiate_decl (result, /*defer_ok=*/true,
 		      /*expl_inst_class_mem_p=*/false);
 }
 
@@ -21979,7 +21981,7 @@ instantiate_class_member (tree decl, int
 {
   mark_decl_instantiated (decl, extern_p);
   if (! extern_p)
-    instantiate_decl (decl, /*defer_ok=*/1,
+    instantiate_decl (decl, /*defer_ok=*/true,
 		      /*expl_inst_class_mem_p=*/true);
 }
 
@@ -22400,15 +22402,17 @@ maybe_instantiate_noexcept (tree fn)
 }
 
 /* Produce the definition of D, a _DECL generated from a template.  If
-   DEFER_OK is nonzero, then we don't have to actually do the
+   DEFER_OK is true, then we don't have to actually do the
    instantiation now; we just have to do it sometime.  Normally it is
    an error if this is an explicit instantiation but D is undefined.
-   EXPL_INST_CLASS_MEM_P is true iff D is a member of an
-   explicitly instantiated class template.  */
+   EXPL_INST_CLASS_MEM_P is true iff D is a member of an explicitly
+   instantiated class template.  If LAMBDA_SUBST is true, we're
+   instantiating a lambda body during instantiating its containing
+   function.  */
 
 tree
-instantiate_decl (tree d, int defer_ok,
-		  bool expl_inst_class_mem_p)
+instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p,
+		  bool lambda_subst)
 {
   tree tmpl = DECL_TI_TEMPLATE (d);
   tree gen_args;
@@ -22423,8 +22427,7 @@ instantiate_decl (tree d, int defer_ok,
   int saved_inhibit_evaluation_warnings = c_inhibit_evaluation_warnings;
   bool external_p;
   bool deleted_p;
-  tree fn_context;
-  bool nested = false;
+  bool push_to_top = false, nested = false;
 
   /* This function should only be used to instantiate templates for
      functions and static member variables.  */
@@ -22439,7 +22442,7 @@ instantiate_decl (tree d, int defer_ok,
      if the variable has a constant value the referring expression can
      take advantage of that fact.  */
   if (VAR_P (d))
-    defer_ok = 0;
+    defer_ok = false;
 
   /* Don't instantiate cloned functions.  Instead, instantiate the
      functions they cloned.  */
@@ -22663,13 +22666,17 @@ instantiate_decl (tree d, int defer_ok,
 	goto out;
     }
 
-  fn_context = decl_function_context (d);
+  /* A LAMBDA will have function context, LAMBDA_SUBST tells us whether we
+     want to keep the local scope (instantiating the containing
+     template function), or not (instantiating the lambda itself).  */
+  push_to_top = ((!lambda_subst && LAMBDA_FUNCTION_P (d))
+		 || !decl_function_context (d));
   nested = (current_function_decl != NULL_TREE);
   vec<tree> omp_privatization_save;
   if (nested)
     save_omp_privatization_clauses (omp_privatization_save);
 
-  if (!fn_context)
+  if (push_to_top)
     push_to_top_level ();
   else
     {
@@ -22751,7 +22758,7 @@ instantiate_decl (tree d, int defer_ok,
 	block = push_stmt_list ();
       else
 	{
-	  if (LAMBDA_FUNCTION_P (d))
+	  if (!lambda_subst && LAMBDA_FUNCTION_P (d))
 	    {
 	      /* When instantiating a lambda's templated function
 		 operator, we need to push the non-lambda class scope
@@ -22844,7 +22851,7 @@ instantiate_decl (tree d, int defer_ok,
   /* We're not deferring instantiation any more.  */
   TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (d)) = 0;
 
-  if (!fn_context)
+  if (push_to_top)
     pop_from_top_level ();
   else if (nested)
     pop_function_context ();
@@ -22914,7 +22921,7 @@ instantiate_pending_templates (int retri
 			 fn = TREE_CHAIN (fn))
 		      if (! DECL_ARTIFICIAL (fn))
 			instantiate_decl (fn,
-					  /*defer_ok=*/0,
+					  /*defer_ok=*/false,
 					  /*expl_inst_class_mem_p=*/false);
 		  if (COMPLETE_TYPE_P (instantiation))
 		    reconsider = 1;
@@ -22929,7 +22936,7 @@ instantiate_pending_templates (int retri
 		{
 		  instantiation
 		    = instantiate_decl (instantiation,
-					/*defer_ok=*/0,
+					/*defer_ok=*/false,
 					/*expl_inst_class_mem_p=*/false);
 		  if (DECL_TEMPLATE_INSTANTIATED (instantiation))
 		    reconsider = 1;
Index: testsuite/g++.dg/cpp1y/pr67273.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr67273.C	(revision 0)
+++ testsuite/g++.dg/cpp1y/pr67273.C	(working copy)
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++14 } }
+// { dg-additional-options "-Wshadow" }
+
+// pr67273 bogus warning about shadowing.
+
+
+template <typename T> void Foo (T &&lambda)
+{
+  int ARG = 2;
+  lambda (1);
+}
+
+void Baz ()
+{
+  Foo ([] (auto &&ARG) {});
+}
Index: testsuite/g++.dg/cpp1y/pr79253.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr79253.C	(revision 0)
+++ testsuite/g++.dg/cpp1y/pr79253.C	(working copy)
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++14 } }
+// PR 79253 ICE instantiating lambda body.
+
+template <typename> struct A;
+template <typename = A<int>> class B {};
+template <class T, class U, class V> void foo (U, V) { T (0, 0); }
+struct C {};
+template <template <bool, bool, bool> class F, class>
+void
+bar ()
+{
+  F<0, 0, 0>::baz;
+}
+struct G { int l; };
+template <int, int, int> struct E : C
+{
+  E (int, int) : e (0)
+  {
+    auto &m = this->m ();
+    auto c = [&] { m.l; };
+  }
+  G &m ();
+  int e;
+};
+struct D
+{
+  void
+  baz () { bar<F, B<>>; }
+  template <bool, bool, bool> struct F
+  {
+    static B<> baz () { foo<E<0, 0, 0>> (0, 0); }
+  };
+};

Reply via email to