c++: Function type and parameter type disagreements [PR92010]
authorPatrick Palka <ppalka@redhat.com>
Mon, 30 Mar 2020 23:55:03 +0000 (19:55 -0400)
committerPatrick Palka <ppalka@redhat.com>
Wed, 8 Apr 2020 14:13:32 +0000 (10:13 -0400)
This resolves parts of Core issues 1001/1322 by rebuilding the function type
of an instantiated function template in terms of its formal parameter types
whenever the original function type and formal parameter types disagree about
the type of a parameter after substitution.

gcc/cp/ChangeLog:

Core issues 1001 and 1322
PR c++/92010
* pt.c (rebuild_function_or_method_type): Split function out from ...
(tsubst_function_type): ... here.
(maybe_rebuild_function_decl_type): New function.
(tsubst_function_decl): Use it.

gcc/testsuite/ChangeLog:

Core issues 1001 and 1322
PR c++/92010
* g++.dg/cpp2a/lambda-uneval11.c: New test.
* g++.dg/template/array33.C: New test.
* g++.dg/template/array34.C: New test.
* g++.dg/template/defarg22.C: New test.

gcc/cp/ChangeLog
gcc/cp/pt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/array33.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/array34.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/defarg22.C [new file with mode: 0644]

index 23fb6ad0ff545651830c362fe1c7af48190d7270..cedd157dbf9795d0bbf45dc43e7e57432520b088 100644 (file)
@@ -1,3 +1,12 @@
+2020-04-08  Patrick Palka  <ppalka@redhat.com>
+
+       Core issues 1001 and 1322
+       PR c++/92010
+       * pt.c (rebuild_function_or_method_type): Split function out from ...
+       (tsubst_function_type): ... here.
+       (maybe_rebuild_function_decl_type): New function.
+       (tsubst_function_decl): Use it.
+
 2020-04-08  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/94325
index 6122227c22f483558d39b6c78ecfb9e8db66cbec..256a937eace799f87ea2d077b2f92f862c859346 100644 (file)
@@ -13475,6 +13475,116 @@ lookup_explicit_specifier (tree v)
   return *explicit_specifier_map->get (v);
 }
 
+/* Given T, a FUNCTION_TYPE or METHOD_TYPE, construct and return a corresponding
+   FUNCTION_TYPE or METHOD_TYPE whose return type is RETURN_TYPE, argument types
+   are ARG_TYPES, and exception specification is RAISES, and otherwise is
+   identical to T.  */
+
+static tree
+rebuild_function_or_method_type (tree t, tree return_type, tree arg_types,
+                                tree raises, tsubst_flags_t complain)
+{
+  gcc_assert (FUNC_OR_METHOD_TYPE_P (t));
+
+  tree new_type;
+  if (TREE_CODE (t) == FUNCTION_TYPE)
+    {
+      new_type = build_function_type (return_type, arg_types);
+      new_type = apply_memfn_quals (new_type, type_memfn_quals (t));
+    }
+  else
+    {
+      tree r = TREE_TYPE (TREE_VALUE (arg_types));
+      /* Don't pick up extra function qualifiers from the basetype.  */
+      r = cp_build_qualified_type_real (r, type_memfn_quals (t), complain);
+      if (! MAYBE_CLASS_TYPE_P (r))
+       {
+         /* [temp.deduct]
+
+            Type deduction may fail for any of the following
+            reasons:
+
+            -- Attempting to create "pointer to member of T" when T
+            is not a class type.  */
+         if (complain & tf_error)
+           error ("creating pointer to member function of non-class type %qT",
+                  r);
+         return error_mark_node;
+       }
+
+      new_type = build_method_type_directly (r, return_type,
+                                            TREE_CHAIN (arg_types));
+    }
+  new_type = cp_build_type_attribute_variant (new_type, TYPE_ATTRIBUTES (t));
+
+  cp_ref_qualifier rqual = type_memfn_rqual (t);
+  bool late_return_type_p = TYPE_HAS_LATE_RETURN_TYPE (t);
+  return build_cp_fntype_variant (new_type, rqual, raises, late_return_type_p);
+}
+
+/* Check if the function type of DECL, a FUNCTION_DECL, agrees with the type of
+   each of its formal parameters.  If there is a disagreement then rebuild
+   DECL's function type according to its formal parameter types, as part of a
+   resolution for Core issues 1001/1322.  */
+
+static void
+maybe_rebuild_function_decl_type (tree decl)
+{
+  bool function_type_needs_rebuilding = false;
+  if (tree parm_list = FUNCTION_FIRST_USER_PARM (decl))
+    {
+      tree parm_type_list = FUNCTION_FIRST_USER_PARMTYPE (decl);
+      while (parm_type_list && parm_type_list != void_list_node)
+       {
+         tree parm_type = TREE_VALUE (parm_type_list);
+         tree formal_parm_type_unqual = strip_top_quals (TREE_TYPE (parm_list));
+         if (!same_type_p (parm_type, formal_parm_type_unqual))
+           {
+             function_type_needs_rebuilding = true;
+             break;
+           }
+
+         parm_list = DECL_CHAIN (parm_list);
+         parm_type_list = TREE_CHAIN (parm_type_list);
+       }
+    }
+
+  if (!function_type_needs_rebuilding)
+    return;
+
+  const tree fntype = TREE_TYPE (decl);
+  tree parm_list = DECL_ARGUMENTS (decl);
+  tree old_parm_type_list = TYPE_ARG_TYPES (fntype);
+  tree new_parm_type_list = NULL_TREE;
+  tree *q = &new_parm_type_list;
+  for (int skip = num_artificial_parms_for (decl); skip > 0; skip--)
+    {
+      *q = copy_node (old_parm_type_list);
+      parm_list = DECL_CHAIN (parm_list);
+      old_parm_type_list = TREE_CHAIN (old_parm_type_list);
+      q = &TREE_CHAIN (*q);
+    }
+  while (old_parm_type_list && old_parm_type_list != void_list_node)
+    {
+      *q = copy_node (old_parm_type_list);
+      tree *new_parm_type = &TREE_VALUE (*q);
+      tree formal_parm_type_unqual = strip_top_quals (TREE_TYPE (parm_list));
+      if (!same_type_p (*new_parm_type, formal_parm_type_unqual))
+       *new_parm_type = formal_parm_type_unqual;
+
+      parm_list = DECL_CHAIN (parm_list);
+      old_parm_type_list = TREE_CHAIN (old_parm_type_list);
+      q = &TREE_CHAIN (*q);
+    }
+  if (old_parm_type_list == void_list_node)
+    *q = void_list_node;
+
+  TREE_TYPE (decl)
+    = rebuild_function_or_method_type (fntype,
+                                      TREE_TYPE (fntype), new_parm_type_list,
+                                      TYPE_RAISES_EXCEPTIONS (fntype), tf_none);
+}
+
 /* Subroutine of tsubst_decl for the case when T is a FUNCTION_DECL.  */
 
 static tree
@@ -13665,6 +13775,8 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
   DECL_ARGUMENTS (r) = parms;
   DECL_RESULT (r) = NULL_TREE;
 
+  maybe_rebuild_function_decl_type (r);
+
   TREE_STATIC (r) = 0;
   TREE_PUBLIC (r) = TREE_PUBLIC (t);
   DECL_EXTERNAL (r) = 1;
@@ -14694,7 +14806,6 @@ tsubst_function_type (tree t,
 {
   tree return_type;
   tree arg_types = NULL_TREE;
-  tree fntype;
 
   /* The TYPE_CONTEXT is not used for function/method types.  */
   gcc_assert (TYPE_CONTEXT (t) == NULL_TREE);
@@ -14765,42 +14876,8 @@ tsubst_function_type (tree t,
     }
 
   /* Construct a new type node and return it.  */
-  if (TREE_CODE (t) == FUNCTION_TYPE)
-    {
-      fntype = build_function_type (return_type, arg_types);
-      fntype = apply_memfn_quals (fntype, type_memfn_quals (t));
-    }
-  else
-    {
-      tree r = TREE_TYPE (TREE_VALUE (arg_types));
-      /* Don't pick up extra function qualifiers from the basetype.  */
-      r = cp_build_qualified_type_real (r, type_memfn_quals (t), complain);
-      if (! MAYBE_CLASS_TYPE_P (r))
-       {
-         /* [temp.deduct]
-
-            Type deduction may fail for any of the following
-            reasons:
-
-            -- Attempting to create "pointer to member of T" when T
-            is not a class type.  */
-         if (complain & tf_error)
-           error ("creating pointer to member function of non-class type %qT",
-                     r);
-         return error_mark_node;
-       }
-
-      fntype = build_method_type_directly (r, return_type,
-                                          TREE_CHAIN (arg_types));
-    }
-  fntype = cp_build_type_attribute_variant (fntype, TYPE_ATTRIBUTES (t));
-
-  /* See comment above.  */
-  tree raises = NULL_TREE;
-  cp_ref_qualifier rqual = type_memfn_rqual (t);
-  fntype = build_cp_fntype_variant (fntype, rqual, raises, late_return_type_p);
-
-  return fntype;
+  return rebuild_function_or_method_type (t, return_type, arg_types,
+                                         /*raises=*/NULL_TREE, complain);
 }
 
 /* FNTYPE is a FUNCTION_TYPE or METHOD_TYPE.  Substitute the template
index c69c8ce11c010053f8da96fa3dc45935ef808b9b..40597ed36580b4823803c73fdd1efb43494c3e8c 100644 (file)
@@ -1,3 +1,12 @@
+2020-04-08  Patrick Palka  <ppalka@redhat.com>
+
+       Core issues 1001 and 1322
+       PR c++/92010
+       * g++.dg/cpp2a/lambda-uneval11.c: New test.
+       * g++.dg/template/array33.C: New test.
+       * g++.dg/template/array34.C: New test.
+       * g++.dg/template/defarg22.C: New test.
+
 2020-04-08  Dennis Zhang  <dennis.zhang@arm.com>
 
        * gcc.target/arm/pragma_cde.c: New test.
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C
new file mode 100644 (file)
index 0000000..a042624
--- /dev/null
@@ -0,0 +1,10 @@
+// PR c++/92010
+// { dg-do compile { target c++2a } }
+
+template <class T> void spam(decltype([]{}) (*s)[sizeof(T)] = nullptr)
+{ }
+
+void foo()
+{
+  spam<int>();
+}
diff --git a/gcc/testsuite/g++.dg/template/array33.C b/gcc/testsuite/g++.dg/template/array33.C
new file mode 100644 (file)
index 0000000..0aa5873
--- /dev/null
@@ -0,0 +1,63 @@
+// Verify that top-level cv-qualifiers on parameter types are considered
+// when determining the function type of an instantiated function template.
+// This resolves a part of Core issues 1001/1322.
+// { dg-do compile }
+// { dg-additional-options "-Wno-volatile" }
+
+template<typename T>
+void foo0(T t = 0);
+
+template<typename T>
+void foo1(const T = 0);
+
+template<typename T>
+void foo2(volatile T t = 0);
+
+template<typename T>
+void foo3(const volatile T t = 0);
+
+#if __cplusplus >= 201103L
+#define SA(X) static_assert(X,#X)
+SA(__is_same(decltype(foo0<char[]>), void(char*)));
+SA(__is_same(decltype(foo0<const char[]>), void(const char*)));
+SA(__is_same(decltype(foo0<volatile char[]>), void(volatile char*)));
+SA(__is_same(decltype(foo0<const volatile char[]>), void(const volatile char*)));
+
+SA(__is_same(decltype(foo1<char[]>), void(const char*)));
+SA(__is_same(decltype(foo1<const char[]>), void(const char*)));
+SA(__is_same(decltype(foo1<volatile char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo1<const volatile char[]>), void(const volatile char*)));
+
+SA(__is_same(decltype(foo2<char[]>), void(volatile char*)));
+SA(__is_same(decltype(foo2<const char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo2<volatile char[]>), void(volatile char*)));
+SA(__is_same(decltype(foo2<const volatile char[]>), void(const volatile char*)));
+
+SA(__is_same(decltype(foo3<char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo3<const char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo3<volatile char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo3<const volatile char[]>), void(const volatile char*)));
+#endif
+
+int main()
+{
+  foo0<char[]>();
+  foo0<const char[]>();
+  foo0<volatile char[]>();
+  foo0<const volatile char[]>();
+
+  foo1<char[]>();
+  foo1<const char[]>();
+  foo1<volatile char[]>();
+  foo1<const volatile char[]>();
+
+  foo2<char[]>();
+  foo2<const char[]>();
+  foo2<volatile char[]>();
+  foo2<const volatile char[]>();
+
+  foo3<char[]>();
+  foo3<const char[]>();
+  foo3<volatile char[]>();
+  foo3<const volatile char[]>();
+}
diff --git a/gcc/testsuite/g++.dg/template/array34.C b/gcc/testsuite/g++.dg/template/array34.C
new file mode 100644 (file)
index 0000000..38c0640
--- /dev/null
@@ -0,0 +1,63 @@
+// Verify that top-level cv-qualifiers on parameter types are considered
+// when determining the function type of an instantiated function template.
+// This resolves a part of Core issues 1001/1322.
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-volatile" }
+
+template<typename... Ts>
+void foo0(Ts... t);
+
+template<typename... Ts>
+void foo1(const Ts... t);
+
+template<typename... Ts>
+void foo2(volatile Ts... t);
+
+template<typename... Ts>
+void foo3(const volatile Ts... t);
+
+#if __cplusplus >= 201103L
+#define SA(X) static_assert(X,#X)
+SA(__is_same(decltype(foo0<char[]>), void(char*)));
+SA(__is_same(decltype(foo0<const char[]>), void(const char*)));
+SA(__is_same(decltype(foo0<volatile char[]>), void(volatile char*)));
+SA(__is_same(decltype(foo0<const volatile char[]>), void(const volatile char*)));
+
+SA(__is_same(decltype(foo1<char[]>), void(const char*)));
+SA(__is_same(decltype(foo1<const char[]>), void(const char*)));
+SA(__is_same(decltype(foo1<volatile char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo1<const volatile char[]>), void(const volatile char*)));
+
+SA(__is_same(decltype(foo2<char[]>), void(volatile char*)));
+SA(__is_same(decltype(foo2<const char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo2<volatile char[]>), void(volatile char*)));
+SA(__is_same(decltype(foo2<const volatile char[]>), void(const volatile char*)));
+
+SA(__is_same(decltype(foo3<char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo3<const char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo3<volatile char[]>), void(const volatile char*)));
+SA(__is_same(decltype(foo3<const volatile char[]>), void(const volatile char*)));
+#endif
+
+int main()
+{
+  foo0<char[]>(0);
+  foo0<const char[]>(0);
+  foo0<volatile char[]>(0);
+  foo0<const volatile char[]>(0);
+
+  foo1<char[]>(0);
+  foo1<const char[]>(0);
+  foo1<volatile char[]>(0);
+  foo1<const volatile char[]>(0);
+
+  foo2<char[]>(0);
+  foo2<const char[]>(0);
+  foo2<volatile char[]>(0);
+  foo2<const volatile char[]>(0);
+
+  foo3<char[]>(0);
+  foo3<const char[]>(0);
+  foo3<volatile char[]>(0);
+  foo3<const volatile char[]>(0);
+}
diff --git a/gcc/testsuite/g++.dg/template/defarg22.C b/gcc/testsuite/g++.dg/template/defarg22.C
new file mode 100644 (file)
index 0000000..599061c
--- /dev/null
@@ -0,0 +1,13 @@
+// PR c++/92010
+// { dg-do compile { target c++11 } }
+
+template <typename T = char[3]>
+void foo(const T t = "; ")
+{
+}
+
+int main()
+{
+  foo ();
+}
+