public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: gcc-patches@gcc.gnu.org
Cc: jason@redhat.com, Patrick Palka <ppalka@redhat.com>
Subject: [PATCH] c++: NTTP object wrapper substitution fixes [PR103346, ...]
Date: Tue,  6 Dec 2022 12:57:02 -0500	[thread overview]
Message-ID: <20221206175702.987794-1-ppalka@redhat.com> (raw)

This patch fixes some issues with substitution into a C++20 template
parameter object wrapper:

* The first testcase demonstrates a situation where the same_type_p
  assert in relevant case of tsubst_copy doesn't hold, because (partial)
  substitution of {int,} into the VIEW_CONVERT_EXPR wrapper yields
  A<int> but substitution into the underlying TEMPLATE_PARM_INDEX is a
  nop and yields A<T> due to tsubst's level == 1 early exit test.  So
  this patch just gets rid of the assert; the type mismatch doesn't
  seem to be a problem in practice since the coercion is from one
  dependent type to another.

* In the second testcase, dependent substitution into the underlying
  TEMPLATE_PARM_INDEX yields a CALL_EXPR with empty TREE_TYPE, which
  tsubst_copy doesn't expect.  This patch fixes this by handling empty
  TREE_TYPE the same way as a non-const TREE_TYPE.  Moreover, after
  this substitution we're left with a VIEW_CONVERT_EXPR wrapping a
  CALL_EXPR instead of a TEMPLATE_PARM_INDEX, which during the subsequent
  non-dependent substitution tsubst_copy doesn't expect either.  So
  this patch also relaxes the tsubst_copy case to accept such
  VIEW_CONVERT_EXPR too.

* In the third testcase, we end up never resolving the call to
  f.modify() since tsubst_copy doesn't do overload resolution.
  This patch fixes this by moving the handling of these
  VIEW_CONVERT_EXPR wrappers from tsubst_copy to tsubst_copy_and_build.
  And it turns out (at least according to our testsuite) that
  tsubst_copy doesn't directly need to handle the other kinds of
  NON_LVALUE_EXPR and VIEW_CONVERT_EXPR, so this patch also gets rid
  of the location_wrapper_p handling from tsubst_copy and moves the
  REF_PARENTHESIZED_P handling to tsubst_copy_and_build.

After this patch, VIEW_CONVERT_EXPR substitution is ultimately just
moved from tsubst_copy to tsubst_copy_and_build and made more
permissive.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

	PR c++/103346
	PR c++/104278
	PR c++/102553

gcc/cp/ChangeLog:

	* pt.cc (tsubst_copy) <case NON_LVALUE_EXPR, VIEW_CONVERT_EXPR>:
	Remove same_type_p assert.  Accept non-TEMPLATE_PARM_INDEX inner
	operand.  Handle empty TREE_TYPE on substituted inner operand.
	Move all of this handling to ...
	(tsubst_copy_and_build): ... here and simplify accordingly.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/nontype-class52a.C: New test.
	* g++.dg/cpp2a/nontype-class53.C: New test.
	* g++.dg/cpp2a/nontype-class54.C: New test.
	* g++.dg/cpp2a/nontype-class55.C: New test.
---
 gcc/cp/pt.cc                                  | 100 +++++++-----------
 gcc/testsuite/g++.dg/cpp2a/nontype-class52a.C |  15 +++
 gcc/testsuite/g++.dg/cpp2a/nontype-class53.C  |  25 +++++
 gcc/testsuite/g++.dg/cpp2a/nontype-class54.C  |  23 ++++
 gcc/testsuite/g++.dg/cpp2a/nontype-class55.C  |  15 +++
 5 files changed, 119 insertions(+), 59 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class52a.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class53.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class54.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class55.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 2d8e4fdd4b5..021c2be9257 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17257,59 +17257,6 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	/* Ordinary template template argument.  */
 	return t;
 
-    case NON_LVALUE_EXPR:
-    case VIEW_CONVERT_EXPR:
-	{
-	  /* Handle location wrappers by substituting the wrapped node
-	     first, *then* reusing the resulting type.  Doing the type
-	     first ensures that we handle template parameters and
-	     parameter pack expansions.  */
-	  if (location_wrapper_p (t))
-	    {
-	      tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args,
-				      complain, in_decl);
-	      return maybe_wrap_with_location (op0, EXPR_LOCATION (t));
-	    }
-	  tree op = TREE_OPERAND (t, 0);
-	  if (code == VIEW_CONVERT_EXPR
-	      && TREE_CODE (op) == TEMPLATE_PARM_INDEX)
-	    {
-	      /* Wrapper to make a C++20 template parameter object const.  */
-	      op = tsubst_copy (op, args, complain, in_decl);
-	      if (!CP_TYPE_CONST_P (TREE_TYPE (op)))
-		{
-		  /* The template argument is not const, presumably because
-		     it is still dependent, and so not the const template parm
-		     object.  */
-		  tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
-		  gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
-				       (type, TREE_TYPE (op)));
-		  if (TREE_CODE (op) == CONSTRUCTOR
-		      || TREE_CODE (op) == IMPLICIT_CONV_EXPR)
-		    {
-		      /* Don't add a wrapper to these.  */
-		      op = copy_node (op);
-		      TREE_TYPE (op) = type;
-		    }
-		  else
-		    /* Do add a wrapper otherwise (in particular, if op is
-		       another TEMPLATE_PARM_INDEX).  */
-		    op = build1 (code, type, op);
-		}
-	      return op;
-	    }
-	  /* force_paren_expr can also create a VIEW_CONVERT_EXPR.  */
-	  else if (code == VIEW_CONVERT_EXPR && REF_PARENTHESIZED_P (t))
-	    {
-	      op = tsubst_copy (op, args, complain, in_decl);
-	      op = build1 (code, TREE_TYPE (op), op);
-	      REF_PARENTHESIZED_P (op) = true;
-	      return op;
-	    }
-	  /* We shouldn't see any other uses of these in templates.  */
-	  gcc_unreachable ();
-	}
-
     case CAST_EXPR:
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
@@ -21568,13 +21515,48 @@ tsubst_copy_and_build (tree t,
       RETURN (t);
 
     case NON_LVALUE_EXPR:
+      gcc_checking_assert (location_wrapper_p (t));
+      RETURN (maybe_wrap_with_location (RECUR (TREE_OPERAND (t, 0)),
+					EXPR_LOCATION (t)));
+
     case VIEW_CONVERT_EXPR:
-      if (location_wrapper_p (t))
-	/* We need to do this here as well as in tsubst_copy so we get the
-	   other tsubst_copy_and_build semantics for a PARM_DECL operand.  */
-	RETURN (maybe_wrap_with_location (RECUR (TREE_OPERAND (t, 0)),
-					  EXPR_LOCATION (t)));
-      /* fallthrough.  */
+      {
+	if (location_wrapper_p (t))
+	  RETURN (maybe_wrap_with_location (RECUR (TREE_OPERAND (t, 0)),
+					    EXPR_LOCATION (t)));
+	tree op = TREE_OPERAND (t, 0);
+	if (REF_PARENTHESIZED_P (t))
+	  {
+	    /* force_paren_expr can also create a VIEW_CONVERT_EXPR.  */
+	    op = RECUR (op);
+	    op = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (op), op);
+	    REF_PARENTHESIZED_P (op) = true;
+	    RETURN (op);
+	  }
+	/* We're dealing with a wrapper to make a C++20 template parameter
+	   object const.  */
+	op = RECUR (op);
+	if (TREE_TYPE (op) == NULL_TREE
+	    || !CP_TYPE_CONST_P (TREE_TYPE (op)))
+	  {
+	    /* The template argument is not const, presumably because
+	       it is still dependent, and so not the const template parm
+	       object.  */
+	    tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+	    if (TREE_CODE (op) == CONSTRUCTOR
+		|| TREE_CODE (op) == IMPLICIT_CONV_EXPR)
+	      {
+		/* Don't add a wrapper to these.  */
+		op = copy_node (op);
+		TREE_TYPE (op) = type;
+	      }
+	    else
+	      /* Do add a wrapper otherwise (in particular, if op is
+		 another TEMPLATE_PARM_INDEX).  */
+	      op = build1 (VIEW_CONVERT_EXPR, type, op);
+	  }
+	  RETURN (op);
+      }
 
     default:
       /* Handle Objective-C++ constructs, if appropriate.  */
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class52a.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class52a.C
new file mode 100644
index 00000000000..ae5d5df70ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class52a.C
@@ -0,0 +1,15 @@
+// A version of nontype-class52.C where explicit template arguments are
+// given in the call to f (which during deduction need to be partially
+// substituted into the NTTP object V in f's signature).
+// { dg-do compile { target c++20 } }
+
+template<class> struct A { };
+
+template<auto> struct B { };
+
+template<class T, A<T> V> void f(B<V>);
+
+int main() {
+  constexpr A<int> a;
+  f<int>(B<a>{});
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class53.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class53.C
new file mode 100644
index 00000000000..9a6398c5f57
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class53.C
@@ -0,0 +1,25 @@
+// PR c++/103346
+// { dg-do compile { target c++20 } }
+
+struct Item {};
+
+template<class T, T... ts>
+struct Sequence { };
+
+template<Item... items>
+using ItemSequence = Sequence<Item, items...>;
+
+template<Item... items>
+constexpr auto f() {
+  constexpr auto l = [](Item item) { return item; };
+  return ItemSequence<l(items)...>{};
+}
+
+using ty0 = decltype(f<>());
+using ty0 = ItemSequence<>;
+
+using ty1 = decltype(f<{}>());
+using ty1 = ItemSequence<{}>;
+
+using ty3 = decltype(f<{}, {}, {}>());
+using ty3 = ItemSequence<{}, {}, {}>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class54.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class54.C
new file mode 100644
index 00000000000..8127b1f5426
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class54.C
@@ -0,0 +1,23 @@
+// PR c++/104278
+// { dg-do compile { target c++20 } }
+
+struct foo {
+  int value;
+  constexpr foo modify() const { return { value + 1 }; }
+};
+
+template<foo f, bool Enable = f.value & 1>
+struct bar {
+  static void run() { }
+};
+
+template<foo f>
+struct qux {
+  static void run() {
+    bar<f.modify()>::run();
+  }
+};
+
+int main() {
+  qux<foo{}>::run();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class55.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class55.C
new file mode 100644
index 00000000000..afcb3d64495
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class55.C
@@ -0,0 +1,15 @@
+// PR c++/102553
+// { dg-do compile { target c++20 } }
+
+struct s1{};
+template<int> inline constexpr s1 ch{};
+
+template<s1 f> struct s2{};
+template<s1 f> using alias1 = s2<f>;
+
+template<class T>
+void general(int n) {
+  alias1<ch<1>>{};
+}
+
+template void general<int>(int);
-- 
2.39.0.rc2


             reply	other threads:[~2022-12-06 17:57 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-06 17:57 Patrick Palka [this message]
2022-12-06 18:35 ` Patrick Palka
2022-12-19 16:01   ` Patrick Palka
2022-12-19 17:17   ` Jason Merrill
2022-12-19 18:13     ` Patrick Palka
2022-12-19 19:56       ` Jason Merrill

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=20221206175702.987794-1-ppalka@redhat.com \
    --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).