Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

We've had a problem with lost conversions to template parameter types for a
while now; looking at this PR, it occurred to me that the problem is really
with alias (and concept) templates, since we do substitution of dependent
arguments into them in a way that we don't for other templates.  And fixing
that specific problem is a lot simpler than adding IMPLICIT_CONV_EXPR around
all dependent template arguments the way I gave up on for 111357.

The other part of the fix was changing tsubst_expr to actually call
convert_nontype_argument instead of assuming it will eventually happen.

I waffled about stripping the forced conversion when !force_conv
vs. skipping them in iterative_hash_template_arg and
template_args_equal (like we already do for some other conversions) and
decided to go with the former, but that isn't a strong preference if it
turns out to be somehow problematic.

        PR c++/112632
        PR c++/112594
        PR c++/111357
        PR c++/104594
        PR c++/67898

gcc/cp/ChangeLog:

        * cp-tree.h (IMPLICIT_CONV_EXPR_FORCED): New.
        * pt.cc (expand_integer_pack): Remove 111357 workaround.
        (maybe_convert_nontype_argument): Add force parm.
        (convert_template_argument): Handle alias template args
        specially.
        (tsubst_expr): Don't ignore IMPLICIT_CONV_EXPR_NONTYPE_ARG.
        * error.cc (dump_expr) [CASE_CONVERT]: Handle null optype.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/alias-decl-nontype1.C: New test.
        * g++.dg/cpp2a/concepts-narrowing1.C: New test.
        * g++.dg/cpp2a/nontype-class63.C: New test.
        * g++.dg/cpp2a/nontype-class63a.C: New test.
---
 gcc/cp/cp-tree.h                              |  5 ++
 gcc/cp/error.cc                               |  4 +-
 gcc/cp/pt.cc                                  | 48 +++++++++++++------
 .../g++.dg/cpp0x/alias-decl-nontype1.C        |  9 ++++
 .../g++.dg/cpp2a/concepts-narrowing1.C        | 16 +++++++
 gcc/testsuite/g++.dg/cpp2a/nontype-class63.C  | 24 ++++++++++
 gcc/testsuite/g++.dg/cpp2a/nontype-class63a.C | 24 ++++++++++
 7 files changed, 114 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-nontype1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-narrowing1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class63.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class63a.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d9b14d7c4f5..60e6dafc549 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4717,6 +4717,11 @@ get_vec_init_expr (tree t)
 #define IMPLICIT_CONV_EXPR_BRACED_INIT(NODE) \
   (TREE_LANG_FLAG_2 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
 
+/* True if NODE represents a conversion forced to be represented in
+   maybe_convert_nontype_argument, i.e. for an alias template.  */
+#define IMPLICIT_CONV_EXPR_FORCED(NODE) \
+  (TREE_LANG_FLAG_3 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
+
 /* Nonzero means that an object of this type cannot be initialized using
    an initializer list.  */
 #define CLASSTYPE_NON_AGGREGATE(NODE) \
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 52e24fb086c..d3fcac70ea1 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2673,6 +2673,8 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
 
        tree ttype = TREE_TYPE (t);
        tree optype = TREE_TYPE (op);
+       if (!optype)
+         optype = unknown_type_node;
 
        if (TREE_CODE (ttype) != TREE_CODE (optype)
            && INDIRECT_TYPE_P (ttype)
@@ -2691,7 +2693,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
            else
              dump_unary_op (pp, "&", t, flags);
          }
-       else if (!same_type_p (TREE_TYPE (op), TREE_TYPE (t)))
+       else if (!same_type_p (optype, ttype))
          {
            /* It is a cast, but we cannot tell whether it is a
               reinterpret or static cast. Use the C style notation.  */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index f82d018c981..fbbca469219 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -3760,13 +3760,6 @@ expand_integer_pack (tree call, tree args, 
tsubst_flags_t complain,
     {
       if (hi != ohi)
        {
-         /* Work around maybe_convert_nontype_argument not doing this for
-            dependent arguments.  Don't use IMPLICIT_CONV_EXPR_NONTYPE_ARG
-            because that will make tsubst_expr ignore it.  */
-         tree type = tsubst (TREE_TYPE (ohi), args, complain, in_decl);
-         if (!TREE_TYPE (hi) || !same_type_p (type, TREE_TYPE (hi)))
-           hi = build1 (IMPLICIT_CONV_EXPR, type, hi);
-
          call = copy_node (call);
          CALL_EXPR_ARG (call, 0) = hi;
        }
@@ -8457,23 +8450,30 @@ convert_wildcard_argument (tree parm, tree arg)
    conversion for the benefit of cp_tree_equal.  */
 
 static tree
-maybe_convert_nontype_argument (tree type, tree arg)
+maybe_convert_nontype_argument (tree type, tree arg, bool force)
 {
   /* Auto parms get no conversion.  */
   if (type_uses_auto (type))
     return arg;
+  /* ??? Do we need to push the IMPLICIT_CONV_EXPR into the pack expansion?
+     That would complicate other things, and it doesn't seem necessary.  */
+  if (TREE_CODE (arg) == EXPR_PACK_EXPANSION)
+    return arg;
   /* We don't need or want to add this conversion now if we're going to use the
      argument for deduction.  */
-  if (value_dependent_expression_p (arg))
+  if (!value_dependent_expression_p (arg))
+    force = false;
+  else if (!force)
     return arg;
 
   type = cv_unqualified (type);
   tree argtype = TREE_TYPE (arg);
-  if (same_type_p (type, argtype))
+  if (argtype && same_type_p (type, argtype))
     return arg;
 
   arg = build1 (IMPLICIT_CONV_EXPR, type, arg);
   IMPLICIT_CONV_EXPR_NONTYPE_ARG (arg) = true;
+  IMPLICIT_CONV_EXPR_FORCED (arg) = force;
   return arg;
 }
 
@@ -8741,6 +8741,22 @@ convert_template_argument (tree parm,
       if (t != TREE_TYPE (parm))
        t = canonicalize_type_argument (t, complain);
 
+      /* We need to handle arguments for alias or concept templates
+        differently: we need to force building an IMPLICIT_CONV_EXPR, because
+        these arguments are going to be substituted directly into the
+        dependent type; they might not get another chance at
+        convert_nontype_argument.  But if the argument ends up here again for
+        a template that isn't one of those, remove the conversion for
+        consistency between naming the same dependent type directly or through
+        an alias.  */
+      bool force_conv = in_decl && (DECL_ALIAS_TEMPLATE_P (in_decl)
+                                   || concept_definition_p (in_decl));
+      if (!force_conv
+         && TREE_CODE (orig_arg) == IMPLICIT_CONV_EXPR
+         && IMPLICIT_CONV_EXPR_FORCED (orig_arg)
+         && same_type_p (TREE_TYPE (orig_arg), t))
+       orig_arg = TREE_OPERAND (orig_arg, 0);
+
       if (!type_dependent_expression_p (orig_arg)
          && !uses_template_parms (t))
        /* We used to call digest_init here.  However, digest_init
@@ -8757,10 +8773,9 @@ convert_template_argument (tree parm,
       else
        {
          val = canonicalize_expr_argument (orig_arg, complain);
-         val = maybe_convert_nontype_argument (t, val);
+         val = maybe_convert_nontype_argument (t, val, force_conv);
        }
 
-
       if (val == NULL_TREE)
        val = error_mark_node;
       else if (val == error_mark_node && (complain & tf_error))
@@ -20056,9 +20071,12 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
            RETURN (retval);
          }
        if (IMPLICIT_CONV_EXPR_NONTYPE_ARG (t))
-         /* We'll pass this to convert_nontype_argument again, we don't need
-            to actually perform any conversion here.  */
-         RETURN (expr);
+         {
+           tree r = convert_nontype_argument (type, expr, complain);
+           if (r == NULL_TREE)
+             r = error_mark_node;
+           RETURN (r);
+         }
        int flags = LOOKUP_IMPLICIT;
        if (IMPLICIT_CONV_EXPR_DIRECT_INIT (t))
          flags = LOOKUP_NORMAL;
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-nontype1.C 
b/gcc/testsuite/g++.dg/cpp0x/alias-decl-nontype1.C
new file mode 100644
index 00000000000..3a9f0de834e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-nontype1.C
@@ -0,0 +1,9 @@
+// PR c++/67898
+// { dg-do compile { target c++11 } }
+
+template<class T, T V, class = decltype(V)> struct A;
+template<class U> using B = A<U, 0>;
+
+using type = A<bool, 0>;
+using type = B<bool>;    // incorrectly resolves to A<bool, 0, int>
+                         // instead of A<bool, 0, bool>
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-narrowing1.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-narrowing1.C
new file mode 100644
index 00000000000..dcfe2729f70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-narrowing1.C
@@ -0,0 +1,16 @@
+// PR c++/104594
+// { dg-do compile { target c++20 } }
+
+template <unsigned char DIM_FROM>
+concept Geometry = (DIM_FROM == -1);
+
+template <class INIT>
+requires Geometry<INIT::n>
+auto GaussNewton(const INIT& init) -> void {}
+
+template<int N>
+struct X {
+  static constexpr int n = N;
+};
+
+int main() { GaussNewton(X<-1>{}); } // { dg-error "no match|narrowing" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class63.C 
b/gcc/testsuite/g++.dg/cpp2a/nontype-class63.C
new file mode 100644
index 00000000000..2e617396912
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class63.C
@@ -0,0 +1,24 @@
+// PR c++/112632
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+inline constexpr bool C = true;
+
+struct n {
+  constexpr n(int a) : i(a) {}
+  int i;
+};
+
+template<n N>
+using get_n_i_type = decltype(N.i);
+
+template<int X>
+int f() {
+  using iii = get_n_i_type<X>;
+#if 1  // Change to 0 and this compiles
+  static_assert(C<iii>);
+#endif
+  return iii{};
+}
+
+template int f<3>();
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class63a.C 
b/gcc/testsuite/g++.dg/cpp2a/nontype-class63a.C
new file mode 100644
index 00000000000..489a90d9fdb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class63a.C
@@ -0,0 +1,24 @@
+// PR c++/112594
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+concept C = true;
+
+struct n {
+  constexpr n(int) {}
+  static int i;
+};
+
+template<n N>
+using get_n_i_type = decltype(N.i);
+
+template<int X>
+int f() {
+  using iii = get_n_i_type<X>;
+#if 1  // Change to 0 and this compiles
+  static_assert(C<iii>);
+#endif
+  return iii{};
+}
+
+template int f<3>();

base-commit: e04376b336502016456eaf4e90c3ea792c77c8df
-- 
2.39.3

Reply via email to