c++: Fix ICE with constrained friend (PR93400).
authorJason Merrill <jason@redhat.com>
Fri, 24 Jan 2020 19:58:56 +0000 (14:58 -0500)
committerJason Merrill <jason@redhat.com>
Sat, 25 Jan 2020 06:07:51 +0000 (01:07 -0500)
Here, the problem was that tsubst_friend_function was modifying the
CONSTRAINT_INFO for the friend template to have the constraints for one
instantiation, which fell down when we went to adjust it for another
instantiation.  Fixed by deferring substitution of trailing requirements
until we try to check declaration matching.

PR c++/93400 - ICE with constrained friend.
* constraint.cc (maybe_substitute_reqs_for): New.
* decl.c (function_requirements_equivalent_p): Call it.
* pt.c (tsubst_friend_function): Only substitute
TEMPLATE_PARMS_CONSTRAINTS.
(tsubst_template_parms): Copy constraints.

gcc/cp/ChangeLog
gcc/cp/constraint.cc
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp2a/concepts-friend3.C
gcc/testsuite/g++.dg/cpp2a/concepts-friend5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C

index 764b9ccffa384d668a4c250a2d2cb4c1ee6a8b0e..ca5676631ad845814f9b2dee14222ef552bb1d86 100644 (file)
@@ -1,3 +1,12 @@
+2020-01-24  Jason Merrill  <jason@redhat.com>
+
+       PR c++/93400 - ICE with constrained friend.
+       * constraint.cc (maybe_substitute_reqs_for): New.
+       * decl.c (function_requirements_equivalent_p): Call it.
+       * pt.c (tsubst_friend_function): Only substitute
+       TEMPLATE_PARMS_CONSTRAINTS.
+       (tsubst_template_parms): Copy constraints.
+
 2020-01-24  Jason Merrill  <jason@redhat.com>
 
        PR c++/93279 - ICE with lambda in member operator.
index 823604afb89e7a06c2d04cc6da07f6af924415e3..cda644eabe272f742d98858c6faf1e50bd264a0e 100644 (file)
@@ -1189,6 +1189,29 @@ remove_constraints (tree t)
     decl_constraints->remove (t);
 }
 
+/* If DECL is a friend, substitute into REQS to produce requirements suitable
+   for declaration matching.  */
+
+tree
+maybe_substitute_reqs_for (tree reqs, const_tree decl_)
+{
+  if (reqs == NULL_TREE)
+    return NULL_TREE;
+  tree decl = CONST_CAST_TREE (decl_);
+  tree result = STRIP_TEMPLATE (decl);
+  if (DECL_FRIEND_P (result))
+    {
+      tree tmpl = decl == result ? DECL_TI_TEMPLATE (result) : decl;
+      tree gargs = generic_targs_for (tmpl);
+      processing_template_decl_sentinel s;
+      if (uses_template_parms (gargs))
+       ++processing_template_decl;
+      reqs = tsubst_constraint (reqs, gargs,
+                               tf_warning_or_error, NULL_TREE);
+    }
+  return reqs;
+}
+
 /* Returns the template-head requires clause for the template
    declaration T or NULL_TREE if none.  */
 
index 77bcf0466089856cb9070a7e7f74a54c8cbd6cef..b8035b4360d9aee18864a89610ffad3f5172af96 100644 (file)
@@ -7832,6 +7832,7 @@ extern void remove_constraints                  (tree);
 extern tree current_template_constraints       (void);
 extern tree associate_classtype_constraints     (tree);
 extern tree build_constraints                   (tree, tree);
+extern tree maybe_substitute_reqs_for          (tree, const_tree);
 extern tree get_template_head_requirements     (tree);
 extern tree get_trailing_function_requirements (tree);
 extern tree get_shorthand_constraints           (tree);
index 98ed79f3579e6aeff744cf1dc41b128921777a36..e55de5dd53df6f122dbbf9e5fd3a755c0c112631 100644 (file)
@@ -942,6 +942,8 @@ function_requirements_equivalent_p (tree newfn, tree oldfn)
   tree reqs2 = get_trailing_function_requirements (oldfn);
   if ((reqs1 != NULL_TREE) != (reqs2 != NULL_TREE))
     return false;
+  reqs1 = maybe_substitute_reqs_for (reqs1, newfn);
+  reqs2 = maybe_substitute_reqs_for (reqs2, oldfn);
   return cp_tree_equal (reqs1, reqs2);
 }
 
index 209044135cb861a090d0d8dd712a01d9dd8e578d..45c204e426930a4f5d23d96ed473d636891244c6 100644 (file)
@@ -10834,29 +10834,12 @@ tsubst_friend_function (tree decl, tree args)
       DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (new_friend))
        = DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (decl));
 
-      /* Attach the template requirements to the new declaration
-         for declaration matching. We need to rebuild the requirements
-         so that parameter levels match.  */
-      if (tree ci = get_constraints (decl))
-       {
-         tree parms = DECL_TEMPLATE_PARMS (new_friend);
-         tree args = generic_targs_for (new_friend);
-         tree treqs = tsubst_constraint (CI_TEMPLATE_REQS (ci), args,
-                                         tf_warning_or_error, NULL_TREE);
-         tree freqs = tsubst_constraint (CI_DECLARATOR_REQS (ci), args,
-                                         tf_warning_or_error, NULL_TREE);
-
-         /* Update the constraints -- these won't really be valid for
-            checking, but that's not what we need them for. These ensure
-            that the declared function can find the friend during
-            declaration matching.  */
-         tree new_ci = get_constraints (new_friend);
-         CI_TEMPLATE_REQS (new_ci) = treqs;
-         CI_DECLARATOR_REQS (new_ci) = freqs;
-
-         /* Also update the template parameter list.  */
-         TEMPLATE_PARMS_CONSTRAINTS (parms) = treqs;
-       }
+      /* Substitute TEMPLATE_PARMS_CONSTRAINTS so that parameter levels will
+        match in decls_match.  */
+      tree parms = DECL_TEMPLATE_PARMS (new_friend);
+      tree treqs = TEMPLATE_PARMS_CONSTRAINTS (parms);
+      treqs = maybe_substitute_reqs_for (treqs, new_friend);
+      TEMPLATE_PARMS_CONSTRAINTS (parms) = treqs;
     }
 
   /* The mangled name for the NEW_FRIEND is incorrect.  The function
@@ -13225,6 +13208,8 @@ tsubst_template_parms (tree parms, tree args, tsubst_flags_t complain)
        tree_cons (size_int (TMPL_PARMS_DEPTH (parms)
                             - TMPL_ARGS_DEPTH (args)),
                   new_vec, NULL_TREE);
+      TEMPLATE_PARMS_CONSTRAINTS (*new_parms)
+       = TEMPLATE_PARMS_CONSTRAINTS (parms);
     }
 
   --processing_template_decl;
index 4f49358ed7d07d0570089da3dbb38297f1325218..4278278b9dfc383503bf84247f6ded588ea237a3 100644 (file)
@@ -4,6 +4,7 @@ template <class T> concept True = true;
 
 template <True U> struct B { int i = ++U::x; };
 template <True U> void f() { ++U::x; }
+template <class U> void g() requires True<U> { ++U::x; }
 
 template <class V> class C
 {
@@ -11,10 +12,12 @@ template <class V> class C
 
   template <True U> friend struct B;
   template <True U> friend void f();
+  template <class U> friend void g() requires True<U>;
 };
 
 int main()
 {
   f<C<int>>();
+  g<C<int>>();
   B<C<int>>();
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend5.C
new file mode 100644 (file)
index 0000000..394f141
--- /dev/null
@@ -0,0 +1,8 @@
+// PR c++/93400
+// { dg-do compile { target concepts } }
+
+template <typename> bool a = true;
+template <typename i> concept b = a<i>;
+template <int> struct f { template <b c> friend auto g(c, f); };
+auto d = f<1>{};
+auto e = f<0>{};
index d901ab20cbff12fffdad89ef0bd8a84cb17ed7e6..83a9083cf17642022b43b0c869c2496069b3448f 100644 (file)
@@ -1,14 +1,13 @@
 // { dg-do run { target c++2a } }
-// { dg-additional-options "-fconcepts-ts" }
 
 template<class, class> constexpr bool is_same_v = false;
 template<class T> constexpr bool is_same_v<T, T> = true;
 
 template<class T, class U>
-concept bool Same = is_same_v<T, U>;
+concept Same = is_same_v<T, U>;
 
 template<class T, class U>
-concept bool Diff = requires(T& t, U& u) { u - t; };
+concept Diff = requires(T& t, U& u) { u - t; };
 
 template<class I, class S>
 int distance(I, S) { return 0; }