On 12/19/22 13:13, Patrick Palka wrote:
On Mon, 19 Dec 2022, Jason Merrill wrote:

On 12/6/22 13:35, Patrick Palka wrote:
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.

We exit in that case because continuing would reduce the level to an
impossible 0.  Why doesn't the preceding code find a binding for T?

Whoops, I misspoke.  The problem is that there's no binding for V since
only T=int was explicitly specified, so when substituting into the
TEMPLATE_PARM_INDEX for V in 'void f(B<V>)', we hit that early exit and
never get a chance to substitute T=int into the tpi's TREE_TYPE (which
would yield A<int> as desired).  So the TREE_TYPE of the wrapped tpi
remains A<T> whereas the substituted TREE_TYPE of the wrapper is A<int>,
a mismatch.

Ah, makes sense.  The patch is OK.


    So
    this patch just gets rid of the assert; the type mismatch doesn't
    seem to be a problem in practice, I suppose because 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 tsubst_copy 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.
    For good measure tsubst_copy_and_build should also handle
    REF_PARENTHESIZED_P wrappers instead of delegating to tsubst_copy.

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 VIEW_CONVERT_EXPR>: In the handling
        of C++20 template parameter object wrappers: Remove same_type_p
        assert.  Accept non-TEMPLATE_PARM_INDEX inner operand.  Handle
        empty TREE_TYPE on substituted inner operand.  Move it to ...
        (tsubst_copy_and_build): ... here.  Also handle REF_PARENTHESIZED_P
        VIEW_CONVERT_EXPRs.

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                                  | 73 ++++++++++---------
   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, 116 insertions(+), 35 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..0a196f069ad 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17271,42 +17271,16 @@ tsubst_copy (tree t, tree args, tsubst_flags_t
complain, tree 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))
+         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.  */
+         /* We shouldn't see any other uses of these in templates
+            (tsubst_copy_and_build handles C++20 tparm object wrappers).  */
          gcc_unreachable ();
        }
   @@ -21569,12 +21543,41 @@ tsubst_copy_and_build (tree t,
         case NON_LVALUE_EXPR:
       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.  */
+      {
+       tree op = RECUR (TREE_OPERAND (t, 0));
+       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 (op, EXPR_LOCATION (t)));
+
+       gcc_checking_assert (TREE_CODE (t) == VIEW_CONVERT_EXPR);
+       if (REF_PARENTHESIZED_P (t))
+         /* force_paren_expr can also create a VIEW_CONVERT_EXPR.  */
+         RETURN (finish_parenthesized_expr (op));
+
+       /* Otherwise, we're dealing with a wrapper to make a C++20 template
+          parameter object const.  */
+       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);




Reply via email to