public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: Patrick Palka <ppalka@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: Fix ICE in tsubst_default_argument [PR92010]
Date: Mon, 6 Apr 2020 11:45:26 -0400 (EDT)	[thread overview]
Message-ID: <alpine.DEB.2.22.413.2004061137390.3094475@idea> (raw)
In-Reply-To: <f175e4bc-5a1d-01f0-13c1-a0398de5d045@redhat.com>

On Wed, 1 Apr 2020, Jason Merrill wrote:

> On 4/1/20 6:29 PM, Jason Merrill wrote:
> > On 3/31/20 3:50 PM, Patrick Palka wrote:
> > > On Tue, 31 Mar 2020, Jason Merrill wrote:
> > > 
> > > > On 3/30/20 6:46 PM, Patrick Palka wrote:
> > > > > On Mon, 30 Mar 2020, Jason Merrill wrote:
> > > > > > On 3/30/20 3:58 PM, Patrick Palka wrote:
> > > > > > > On Thu, 26 Mar 2020, Jason Merrill wrote:
> > > > > > > 
> > > > > > > > On 3/22/20 9:21 PM, Patrick Palka wrote:
> > > > > > > > > This patch relaxes an assertion in tsubst_default_argument
> > > > > > > > > that
> > > > > > > > > exposes
> > > > > > > > > a
> > > > > > > > > latent
> > > > > > > > > bug in how we substitute an array type into a cv-qualified
> > > > > > > > > wildcard
> > > > > > > > > function
> > > > > > > > > parameter type.  Concretely, the latent bug is that given the
> > > > > > > > > function
> > > > > > > > > template
> > > > > > > > > 
> > > > > > > > >       template<typename T> void foo(const T t);
> > > > > > > > > 
> > > > > > > > > one would expect the type of foo<int[]> to be void(const
> > > > > > > > > int*), but
> > > > > > > > > we
> > > > > > > > > (seemingly prematurely) strip function parameter types of
> > > > > > > > > their
> > > > > > > > > top-level
> > > > > > > > > cv-qualifiers when building the function's TYPE_ARG_TYPES, and
> > > > > > > > > instead
> > > > > > > > > end
> > > > > > > > > up
> > > > > > > > > obtaining void(int*) as the type of foo<int[]> after
> > > > > > > > > substitution
> > > > > > > > > and
> > > > > > > > > decaying.
> > > > > > > > > 
> > > > > > > > > We still however correctly substitute into and decay the
> > > > > > > > > formal
> > > > > > > > > parameter
> > > > > > > > > type,
> > > > > > > > > obtaining const int* as the type of t after substitution.  But
> > > > > > > > > this
> > > > > > > > > then
> > > > > > > > > leads
> > > > > > > > > to us tripping over the assert in tsubst_default_argument that
> > > > > > > > > verifies
> > > > > > > > > the
> > > > > > > > > formal parameter type and the function type are consistent.
> > > > > > > > > 
> > > > > > > > > Assuming it's too late at this stage to fix the substitution
> > > > > > > > > bug, we
> > > > > > > > > can
> > > > > > > > > still
> > > > > > > > > relax the assertion like so.  Tested on x86_64-pc-linux-gnu,
> > > > > > > > > does
> > > > > > > > > this
> > > > > > > > > look
> > > > > > > > > OK?
> > > > > > > > 
> > > > > > > > This is core issues 1001/1322, which have not been resolved. 
> > > > > > > > Clang
> > > > > > > > does
> > > > > > > > the
> > > > > > > > substitution the way you suggest; EDG rejects the testcase
> > > > > > > > because the
> > > > > > > > two
> > > > > > > > substitutions produce different results.  I think it would make
> > > > > > > > sense
> > > > > > > > to
> > > > > > > > follow the EDG behavior until this issue is actually resolved.
> > > > > > > 
> > > > > > > Here is what I have so far towards that end.  When substituting
> > > > > > > into the
> > > > > > > PARM_DECLs of a function decl, we now additionally check if the
> > > > > > > aforementioned Core issues are relevant and issue a (fatal)
> > > > > > > diagnostic
> > > > > > > if so.  This patch checks this in tsubst_decl <case PARM_DECL>
> > > > > > > rather
> > > > > > > than in tsubst_function_decl for efficiency reasons, so that we
> > > > > > > don't
> > > > > > > have to perform another traversal over the DECL_ARGUMENTS /
> > > > > > > TYPE_ARG_TYPES just to implement this check.
> > > > > > 
> > > > > > Hmm, this seems like writing more complicated code for a very
> > > > > > marginal
> > > > > > optimization; how many function templates have so many parameters
> > > > > > that
> > > > > > walking
> > > > > > over them once to compare types will have any effect on compile
> > > > > > time?
> > > > > 
> > > > > Good point... though I just tried implementing this check in
> > > > > tsubst_function_decl, and it seems it might be just as complicated to
> > > > > implement it there instead, at least if we want to handle function
> > > > > parameter packs correctly.
> > > > > 
> > > > > If we were to implement this check in tsubst_function_decl, then since
> > > > > we have access to the instantiated function, it would presumably
> > > > > suffice
> > > > > to compare its substituted DECL_ARGUMENTS with its substituted
> > > > > TYPE_ARG_TYPES to see if they're consistent.  Doing so would certainly
> > > > > catch the original testcase, i.e.
> > > > > 
> > > > >     template<typename T>
> > > > >       void foo(const T);
> > > > >     int main() { foo<int[]>(0); }
> > > > > 
> > > > > because the DECL_ARGUMENTS of foo<int[]> would be {const int*} and its
> > > > > TYPE_ARG_TYPES would be {int*}.  But apparently it doesn't catch the
> > > > > corresponding testcase that uses a function parameter pack, i.e.
> > > > > 
> > > > >     template<typename... Ts>
> > > > >       void foo(const Ts...);
> > > > >     int main() { foo<int[]>(0); }
> > > > > 
> > > > > because it turns out we don't strip top-level cv-qualifiers from
> > > > > function parameter packs from TYPE_ARG_TYPES at declaration time, as
> > > > > we
> > > > > do with regular function parameters.  So in this second testcase both
> > > > > DECL_ARGUMENTS and TYPE_ARG_TYPES of foo<int[]> would be {const int*},
> > > > > and yet we would (presumably) want to reject this instantiation too.
> > > > > 
> > > > > So it seems comparing TYPE_ARG_TYPES and DECL_ARGUMENTS from
> > > > > tsubst_function_decl would not suffice, and we would still need to do
> > > > > a
> > > > > variant of the trick that's done in this patch, i.e. substitute into
> > > > > each dependent parameter type stripped of its top-level cv-qualifiers,
> > > > > to see if these cv-qualifiers make a material difference in the
> > > > > resulting function type.  Or maybe there's yet another way to detect
> > > > > this?
> > > > 
> > > > I think let's go ahead with comparing TYPE_ARG_TYPES and DECL_ARGUMENTS;
> > > > the
> > > > problem comes when they disagree.  If we're handling pack expansions
> > > > wrong,
> > > > that's a separate issue.
> > > 
> > > Hm, comparing TYPE_ARG_TYPES and DECL_ARGUMENTS for compatibility seems
> > > to be exposing a latent bug with how we handle lambdas that appear in
> > > function parameter types.  Take g++.dg/cpp2a/lambda-uneval3.C for
> > > example:
> > > 
> > >      template <class T> void spam(decltype([]{}) (*s)[sizeof(T)]) {}
> > >      int main() { spam<char>(nullptr); }
> > > 
> > > According to tsubst_function_decl in current trunk, the type of the
> > > function paremeter 's' of spam<char> according to its TYPE_ARG_TYPES is
> > >      struct ._anon_4[1] *
> > > and according to its DECL_ARGUMENTS the type of 's' is
> > >      struct ._anon_5[1] *
> > > 
> > > The disagreement happens because we call tsubst_lambda_expr twice during
> > > substitution and thereby generate two distinct lambda types, one when
> > > substituting into the TYPE_ARG_TYPES and another when substituting into
> > > the DECL_ARGUMENTS.  I'm not sure how to work around this
> > > bug/false-positive..
> > 
> > Oof.
> > 
> > I think probably the right answer is to rebuild TYPE_ARG_TYPES from
> > DECL_ARGUMENTS if they don't match.
> 
> ...and treat that as a resolution of 1001/1322, so not giving an error.

Is something like this what you have in mind?  Bootstrap and testing in
progress.

-- >8 --

Subject: [PATCH] c++: Rebuild function type when it disagrees with formal
 parameter types [PR92010]

gcc/cp/ChangeLog:

	Core issues 1001 and 1322
	PR c++/92010
	* pt.c (maybe_rebuild_function_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/pt.c                                  | 55 +++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C | 10 ++++
 gcc/testsuite/g++.dg/template/array33.C      | 63 ++++++++++++++++++++
 gcc/testsuite/g++.dg/template/array34.C      | 63 ++++++++++++++++++++
 gcc/testsuite/g++.dg/template/defarg22.C     | 13 ++++
 5 files changed, 204 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C
 create mode 100644 gcc/testsuite/g++.dg/template/array33.C
 create mode 100644 gcc/testsuite/g++.dg/template/array34.C
 create mode 100644 gcc/testsuite/g++.dg/template/defarg22.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 041ce35a31c..fc0df790c0f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13475,6 +13475,59 @@ lookup_explicit_specifier (tree v)
   return *explicit_specifier_map->get (v);
 }
 
+/* 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 new_arg_types = copy_list (TYPE_ARG_TYPES (TREE_TYPE (decl)));
+
+  tree parm_list = FUNCTION_FIRST_USER_PARM (decl);
+  tree old_parm_type_list = FUNCTION_FIRST_USER_PARMTYPE (decl);
+  tree new_parm_type_list = skip_artificial_parms_for (decl, new_arg_types);
+  while (old_parm_type_list && old_parm_type_list != void_list_node)
+    {
+      tree *new_parm_type = &TREE_VALUE (new_parm_type_list);
+      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;
+
+      if (TREE_CHAIN (old_parm_type_list) == void_list_node)
+	TREE_CHAIN (new_parm_type_list) = void_list_node;
+      parm_list = DECL_CHAIN (parm_list);
+      old_parm_type_list = TREE_CHAIN (old_parm_type_list);
+      new_parm_type_list = TREE_CHAIN (new_parm_type_list);
+    }
+
+  TREE_TYPE (decl)
+    = build_function_type (TREE_TYPE (TREE_TYPE (decl)), new_arg_types);
+}
+
 /* Subroutine of tsubst_decl for the case when T is a FUNCTION_DECL.  */
 
 static tree
@@ -13665,6 +13718,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;
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C
new file mode 100644
index 00000000000..a04262494c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval11.C
@@ -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
index 00000000000..0aa587351b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/array33.C
@@ -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
index 00000000000..38c06401974
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/array34.C
@@ -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
index 00000000000..599061cedb0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/defarg22.C
@@ -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 ();
+}
+
-- 
2.26.0.106.g9fadedd637

  reply	other threads:[~2020-04-06 15:45 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-23  1:21 Patrick Palka
2020-03-23  3:11 ` Patrick Palka
2020-03-26 19:29 ` Jason Merrill
2020-03-30 19:58   ` Patrick Palka
2020-03-30 20:15     ` Patrick Palka
2020-03-30 20:42     ` Jason Merrill
2020-03-30 22:46       ` Patrick Palka
2020-03-31 17:13         ` Jason Merrill
2020-03-31 19:50           ` Patrick Palka
2020-04-01 22:29             ` Jason Merrill
2020-04-01 22:37               ` Jason Merrill
2020-04-06 15:45                 ` Patrick Palka [this message]
2020-04-06 21:33                   ` Jason Merrill
2020-04-07 17:40                     ` Patrick Palka
2020-04-07 20:26                       ` Patrick Palka
2020-04-07 21:21                       ` Jason Merrill
2020-04-08 14:18                         ` Patrick Palka

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=alpine.DEB.2.22.413.2004061137390.3094475@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).