PR c++/93286 - ICE with __is_constructible and variadic template.
authorJason Merrill <jason@redhat.com>
Thu, 16 Jan 2020 21:55:39 +0000 (16:55 -0500)
committerJason Merrill <jason@redhat.com>
Fri, 17 Jan 2020 03:24:28 +0000 (22:24 -0500)
Here we had been recursing in tsubst_copy_and_build if type2 was a TREE_LIST
because that function knew how to deal with pack expansions, and tsubst
didn't.  But tsubst_copy_and_build expects to be dealing with expressions,
so we crash when trying to convert_from_reference a type.

* pt.c (tsubst) [TREE_LIST]: Handle pack expansion.
(tsubst_copy_and_build) [TRAIT_EXPR]: Always use tsubst for type2.

gcc/cp/ChangeLog
gcc/cp/pt.c
gcc/testsuite/g++.dg/ext/is_constructible4.C [new file with mode: 0644]

index 3ca5d7a..c37e461 100644 (file)
@@ -1,5 +1,9 @@
 2020-01-16  Jason Merrill  <jason@redhat.com>
 
+       PR c++/93286 - ICE with __is_constructible and variadic template.
+       * pt.c (tsubst) [TREE_LIST]: Handle pack expansion.
+       (tsubst_copy_and_build) [TRAIT_EXPR]: Always use tsubst for type2.
+
        PR c++/93280 - ICE with aggregate assignment and DMI.
        * init.c (get_nsdmi): Set TARGET_EXPR_DIRECT_INIT_P here.
        * typeck2.c (digest_nsdmi_init): Not here.
index 9bb8cc1..872f8ff 100644 (file)
@@ -15350,6 +15350,71 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
        if (t == void_list_node)
          return t;
 
+       if ((TREE_PURPOSE (t) && PACK_EXPANSION_P (TREE_PURPOSE (t)))
+           || (TREE_VALUE (t) && PACK_EXPANSION_P (TREE_VALUE (t))))
+         {
+           /* We have pack expansions, so expand those and
+              create a new list out of it.  */
+
+           /* Expand the argument expressions.  */
+           tree purposevec = NULL_TREE;
+           if (TREE_PURPOSE (t))
+             purposevec = tsubst_pack_expansion (TREE_PURPOSE (t), args,
+                                                 complain, in_decl);
+           if (purposevec == error_mark_node)
+             return error_mark_node;
+
+           tree valuevec = NULL_TREE;
+           if (TREE_VALUE (t))
+             valuevec = tsubst_pack_expansion (TREE_VALUE (t), args,
+                                               complain, in_decl);
+           if (valuevec == error_mark_node)
+             return error_mark_node;
+
+           /* Build the rest of the list.  */
+           tree chain = TREE_CHAIN (t);
+           if (chain && chain != void_type_node)
+             chain = tsubst (chain, args, complain, in_decl);
+           if (chain == error_mark_node)
+             return error_mark_node;
+
+           /* Determine the number of arguments.  */
+           int len = -1;
+           if (purposevec && TREE_CODE (purposevec) == TREE_VEC)
+             {
+               len = TREE_VEC_LENGTH (purposevec);
+               gcc_assert (!valuevec || len == TREE_VEC_LENGTH (valuevec));
+             }
+           else if (TREE_CODE (valuevec) == TREE_VEC)
+             len = TREE_VEC_LENGTH (valuevec);
+           else
+             {
+               /* Since we only performed a partial substitution into
+                  the argument pack, we only RETURN (a single list
+                  node.  */
+               if (purposevec == TREE_PURPOSE (t)
+                   && valuevec == TREE_VALUE (t)
+                   && chain == TREE_CHAIN (t))
+                 return t;
+
+               return tree_cons (purposevec, valuevec, chain);
+             }
+
+           /* Convert the argument vectors into a TREE_LIST.  */
+           for (int i = len; i-- > 0; )
+             {
+               purpose = (purposevec ? TREE_VEC_ELT (purposevec, i)
+                          : NULL_TREE);
+               value = (valuevec ? TREE_VEC_ELT (valuevec, i)
+                        : NULL_TREE);
+
+               /* Build the list (backwards).  */
+               chain = hash_tree_cons (purpose, value, chain);
+             }
+
+           return chain;
+         }
+
        purpose = TREE_PURPOSE (t);
        if (purpose)
          {
@@ -20158,13 +20223,8 @@ tsubst_copy_and_build (tree t,
       {
        tree type1 = tsubst (TRAIT_EXPR_TYPE1 (t), args,
                             complain, in_decl);
-
-       tree type2 = TRAIT_EXPR_TYPE2 (t);
-       if (type2 && TREE_CODE (type2) == TREE_LIST)
-         type2 = RECUR (type2);
-       else if (type2)
-         type2 = tsubst (type2, args, complain, in_decl);
-
+       tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args,
+                            complain, in_decl);
        RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t),
                                   TRAIT_EXPR_KIND (t), type1, type2));
       }
diff --git a/gcc/testsuite/g++.dg/ext/is_constructible4.C b/gcc/testsuite/g++.dg/ext/is_constructible4.C
new file mode 100644 (file)
index 0000000..6dfe3c0
--- /dev/null
@@ -0,0 +1,18 @@
+// PR c++/93286
+// { dg-do compile { target c++14 } }
+
+struct A { static const bool value = true; };
+template <bool> using __bool_constant = A;
+template <typename... _Args>
+struct B : __bool_constant<__is_constructible(int, _Args...)> {};
+template <bool> using enable_if_t = int;
+template <typename... _Args> bool is_constructible_v = B<_Args...>::value;
+class C {
+  template <typename _Tp, typename = enable_if_t<is_constructible_v<_Tp>>>
+  C(_Tp &&);
+};
+using Effect_t = C;
+void fn1(Effect_t effect) {
+  int i;
+  [](int &effect) {}(i);
+}