c++: lambda in template default argument [PR103186]
authorJason Merrill <jason@redhat.com>
Wed, 26 Jan 2022 21:42:57 +0000 (16:42 -0500)
committerJason Merrill <jason@redhat.com>
Tue, 1 Feb 2022 19:14:12 +0000 (14:14 -0500)
The problem with this testcase was that since my patch for PR97900 we
weren't preserving DECL_UID identity for parameters of instantiations of
templated functions, so using those parameters as the keys for the
defarg_inst map broke.  I think this was always fragile given the
possibility of redeclarations, so instead of reverting that change let's
switch to keying off the function.

Memory use compiling stdc++.h is not noticeably different.

PR c++/103186

gcc/cp/ChangeLog:

* pt.cc (defarg_inst): Use tree_vec_map_cache_hasher.
(defarg_insts_for): New.
(tsubst_default_argument): Adjust.

gcc/testsuite/ChangeLog:

* g++.dg/cpp0x/lambda/lambda-defarg10.C: New test.

gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-defarg10.C [new file with mode: 0644]

index feee629..6e129da 100644 (file)
@@ -13676,7 +13676,28 @@ tsubst_aggr_type (tree t,
     }
 }
 
-static GTY((cache)) decl_tree_cache_map *defarg_inst;
+/* Map from a FUNCTION_DECL to a vec of default argument instantiations,
+   indexed in reverse order of the parameters.  */
+
+static GTY((cache)) hash_table<tree_vec_map_cache_hasher> *defarg_inst;
+
+/* Return a reference to the vec* of defarg insts for FN.  */
+
+static vec<tree,va_gc> *&
+defarg_insts_for (tree fn)
+{
+  if (!defarg_inst)
+    defarg_inst = hash_table<tree_vec_map_cache_hasher>::create_ggc (13);
+  tree_vec_map in = { fn, nullptr };
+  tree_vec_map **slot
+    = defarg_inst->find_slot_with_hash (&in, DECL_UID (fn), INSERT);
+  if (!*slot)
+    {
+      *slot = ggc_alloc<tree_vec_map> ();
+      **slot = in;
+    }
+  return (*slot)->to;
+}
 
 /* Substitute into the default argument ARG (a default argument for
    FN), which has the indicated TYPE.  */
@@ -13706,9 +13727,16 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg,
 
   gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, parmtype));
 
-  tree *slot;
-  if (defarg_inst && (slot = defarg_inst->get (parm)))
-    return *slot;
+  /* Remember the location of the pointer to the vec rather than the location
+     of the particular element, in case the vec grows in tsubst_expr.  */
+  vec<tree,va_gc> *&defs = defarg_insts_for (fn);
+  /* Index in reverse order to avoid allocating space for initial parameters
+     that don't have default arguments.  */
+  unsigned ridx = list_length (parm);
+  if (vec_safe_length (defs) < ridx)
+    vec_safe_grow_cleared (defs, ridx);
+  else if (tree inst = (*defs)[ridx - 1])
+    return inst;
 
   /* This default argument came from a template.  Instantiate the
      default argument here, not in tsubst.  In the case of
@@ -13753,11 +13781,7 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg,
   pop_from_top_level ();
 
   if (arg != error_mark_node && !cp_unevaluated_operand)
-    {
-      if (!defarg_inst)
-       defarg_inst = decl_tree_cache_map::create_ggc (37);
-      defarg_inst->put (parm, arg);
-    }
+    (*defs)[ridx - 1] = arg;
 
   return arg;
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-defarg10.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-defarg10.C
new file mode 100644 (file)
index 0000000..e08eb4f
--- /dev/null
@@ -0,0 +1,21 @@
+// PR c++/103186
+// { dg-do compile { target c++11 } }
+
+struct f
+{
+  template<class T1>
+   f(const T1&){}
+};
+
+
+template<typename T> class A {
+public:
+    void foo(A<T> a, const f& fn = [](){}) { }
+    void bar(A<T> a) { foo(a); }
+};
+int main() {
+    A<int> a;
+    a.foo(a);
+    a.bar(a);
+    return 0;
+}