diff mbox

RFA (pointer-set): PATCH for c++/57899 (infinite recursion with std::bind)

Message ID 52EABD69.1020705@redhat.com
State New
Headers show

Commit Message

Jason Merrill Jan. 30, 2014, 9 p.m. UTC
In this testcase, the problem was that one instantiation of 
_Mu::operator() recursively calls another one, and we were finding a 
specialization of the arg parameter in local_specializations from the 
first instantiation, leading to a wrong result and infinite recursion. 
This patch fixes the problem by properly clearing local_specializations 
when we push_to_top_level so that the second instantiation won't find 
the specialization from the first one.

Also, in the past we've had issues with nested functions in templates 
being able to refer to static/const variables from the enclosing 
context; we've dealt with that by forcing repeated lookup, but it would 
be better to keep the local_specializations from the enclosing function 
around so we can find them the usual way.  To that end, I've added a 
pointer_map_copy.

Tested x86_64-pc-linux-gnu.  Is the pointer_map_copy change OK for trunk?
diff mbox

Patch

2014-01-30  Jason Merrill  <jason@redhat.com>

	PR c++/57899
gcc/
	* pointer-set.c (pointer_map_copy): New.
	* pointer-set.h: Declare it.
gcc/cp/
	* cp-tree.h (struct saved_scope): Add x_local_specializations.
	(local_specializations): New macro.
	* pt.c (local_specializations): Remove variable.
	(instantiate_decl): Only create new local_specializations if
	we did push_to_top_level.

diff --git a/gcc/pointer-set.c b/gcc/pointer-set.c
index 8b6a732..fdd9275 100644
--- a/gcc/pointer-set.c
+++ b/gcc/pointer-set.c
@@ -192,6 +192,19 @@  pointer_map_create (void)
   return result;
 }
 
+/* Duplicate an existing pointer map.  */
+struct pointer_map_t *
+pointer_map_copy (pointer_map_t *p)
+{
+  struct pointer_map_t *result = XNEW (struct pointer_map_t);
+  result->pset.n_elements = p->pset.n_elements;
+  result->pset.log_slots = p->pset.log_slots;
+  result->pset.n_slots = p->pset.n_slots;
+  result->pset.slots = XDUPVEC (const void *, p->pset.slots, p->pset.n_slots);
+  result->values = XDUPVEC (void *, p->values, p->pset.n_slots);
+  return result;
+}
+
 /* Reclaims all memory associated with PMAP.  */
 void pointer_map_destroy (struct pointer_map_t *pmap)
 {
diff --git a/gcc/pointer-set.h b/gcc/pointer-set.h
index a426534..67f6527 100644
--- a/gcc/pointer-set.h
+++ b/gcc/pointer-set.h
@@ -159,6 +159,7 @@  pointer_map<T>::traverse (bool (*fn) (const void *, T *, void *), void *data)
 
 struct pointer_map_t;
 pointer_map_t *pointer_map_create (void);
+pointer_map_t *pointer_map_copy (pointer_map_t *);
 void pointer_map_destroy (pointer_map_t *pmap);
 
 void **pointer_map_contains (const pointer_map_t *pmap, const void *p);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7f46499..7681b27 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1049,6 +1049,8 @@  struct GTY(()) saved_scope {
   cp_binding_level *class_bindings;
   cp_binding_level *bindings;
 
+  struct pointer_map_t *x_local_specializations;
+
   struct saved_scope *prev;
 };
 
@@ -1098,6 +1100,12 @@  struct GTY(()) saved_scope {
 
 #define previous_class_level scope_chain->x_previous_class_level
 
+/* A map from local variable declarations in the body of the template
+   presently being instantiated to the corresponding instantiated
+   local variables.  */
+
+#define local_specializations scope_chain->x_local_specializations
+
 /* A list of private types mentioned, for deferred access checking.  */
 
 extern GTY(()) struct saved_scope *scope_chain;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 4a5b6cc..43aeaa0 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -78,11 +78,6 @@  static GTY(()) tree saved_access_scope;
    to the EXPR_STMT that is its result.  */
 static tree cur_stmt_expr;
 
-/* A map from local variable declarations in the body of the template
-   presently being instantiated to the corresponding instantiated
-   local variables.  */
-static struct pointer_map_t *local_specializations;
-
 /* True if we've recursed into fn_type_unification too many times.  */
 static bool excessive_deduction_depth;
 
@@ -19637,7 +19632,15 @@  instantiate_decl (tree d, int defer_ok,
       saved_local_specializations = local_specializations;
 
       /* Set up the list of local specializations.  */
-      local_specializations = pointer_map_create ();
+      if (fn_context
+	  && !(DECL_CLASS_SCOPE_P (d) && LAMBDA_TYPE_P (DECL_CONTEXT (d))))
+	/* Normally in a nested function we want to be able to look up
+	   variables from the enclosing function.  But in a lambda we've
+	   already remapped everything to refer to the closure, and we
+	   might be instantiating from outside the enclosing function.  */
+	local_specializations = pointer_map_copy (local_specializations);
+      else
+	local_specializations = pointer_map_create ();
 
       /* Set up context.  */
       if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)
diff --git a/libstdc++-v3/testsuite/20_util/bind/57899.cc b/libstdc++-v3/testsuite/20_util/bind/57899.cc
new file mode 100644
index 0000000..d46d53e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/bind/57899.cc
@@ -0,0 +1,48 @@ 
+// Copyright (C) 2010-2014 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 20.7.11 Function template bind
+
+// PR c++/57899
+// { dg-do compile }
+// { dg-options -std=c++11 }
+
+#include <functional>
+using std::bind;
+using std::placeholders::_1;
+
+struct S { int i; };
+
+struct P { S s; };
+
+struct get_s
+{
+  const S& operator()(const P& p) const { return p.s; }
+} gs;
+
+int gi(const S& s) { return s.i; }
+
+bool cmp(int, int) { return true; }
+
+int main()
+{
+  P p{};
+  auto f1 = bind(gs, _1);
+  auto f2 = bind(gi, f1);
+  auto f3 = bind(cmp, f2, 5);
+  f3(p);
+}