c++: Allow floating-point template parms in C++20.
authorJason Merrill <jason@redhat.com>
Thu, 2 Jul 2020 19:14:52 +0000 (15:14 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 10 Jul 2020 12:36:50 +0000 (08:36 -0400)
P1907R1 made various adjustments to non-type template parameters, notably
introducing the notion of "structural type".  I implemented an early version
of that specification in r10-4426, but it was adjusted in the final paper to
allow more.  This patch implements allowing template parameters of
floating-point type; still to be implemented are unions and subobjects.

gcc/cp/ChangeLog:

* pt.c (convert_nontype_argument): Handle REAL_TYPE.
(invalid_nontype_parm_type_p): Allow all structural types.
* tree.c (structural_type_p): Use SCALAR_TYPE_P.

gcc/testsuite/ChangeLog:

* g++.dg/cpp0x/pr81246.C: No error in C++20.
* g++.dg/cpp0x/variadic74.C: No error in C++20.
* g++.dg/cpp1z/nontype-auto3.C: No error in C++20.
* g++.dg/template/crash106.C: No error in C++20.
* g++.dg/template/crash119.C: No error in C++20.
* g++.dg/template/nontype12.C: No error in C++20.
* g++.dg/template/void3.C: Don't require follow-on message.
* g++.dg/template/void7.C: Don't require follow-on message.
* g++.dg/template/void9.C: Don't require follow-on message.

12 files changed:
gcc/cp/pt.c
gcc/cp/tree.c
gcc/testsuite/g++.dg/cpp0x/pr81246.C
gcc/testsuite/g++.dg/cpp0x/variadic74.C
gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/crash106.C
gcc/testsuite/g++.dg/template/crash119.C
gcc/testsuite/g++.dg/template/nontype12.C
gcc/testsuite/g++.dg/template/void3.C
gcc/testsuite/g++.dg/template/void7.C
gcc/testsuite/g++.dg/template/void9.C

index b6423f7..61f2273 100644 (file)
@@ -7257,7 +7257,8 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
      For a non-type template-parameter of integral or enumeration type,
      integral promotions (_conv.prom_) and integral conversions
      (_conv.integral_) are applied.  */
-  if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
+  if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
+      || TREE_CODE (type) == REAL_TYPE)
     {
       if (cxx_dialect < cxx11)
        {
@@ -7272,7 +7273,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 
       /* Notice that there are constant expressions like '4 % 0' which
         do not fold into integer constants.  */
-      if (TREE_CODE (expr) != INTEGER_CST && !val_dep_p)
+      if (!CONSTANT_CLASS_P (expr) && !val_dep_p)
        {
          if (complain & tf_error)
            {
@@ -7287,7 +7288,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
                return NULL_TREE;
              /* else cxx_constant_value complained but gave us
                 a real constant, so go ahead.  */
-             if (TREE_CODE (expr) != INTEGER_CST)
+             if (!CONSTANT_CLASS_P (expr))
                {
                  /* Some assemble time constant expressions like
                     (intptr_t)&&lab1 - (intptr_t)&&lab2 or
@@ -7297,7 +7298,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
                     compile time.  Refuse them here.  */
                  gcc_checking_assert (reduced_constant_expression_p (expr));
                  error_at (loc, "template argument %qE for type %qT not "
-                                "a constant integer", expr, type);
+                                "a compile-time constant", expr, type);
                  return NULL_TREE;
                }
            }
@@ -26127,31 +26128,31 @@ invalid_nontype_parm_type_p (tree type, tsubst_flags_t complain)
   else if (cxx_dialect >= cxx11
           && TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM)
     return false;
-  else if (CLASS_TYPE_P (type))
+  else if (VOID_TYPE_P (type))
+    /* Fall through.  */;
+  else if (cxx_dialect >= cxx20)
     {
-      if (cxx_dialect < cxx20)
-       {
-         if (complain & tf_error)
-           error ("non-type template parameters of class type only available "
-                  "with %<-std=c++20%> or %<-std=gnu++20%>");
-         return true;
-       }
       if (dependent_type_p (type))
        return false;
-      if (!complete_type_or_else (type, NULL_TREE))
+      if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
        return true;
-      if (!structural_type_p (type))
+      if (structural_type_p (type))
+       return false;
+      if (complain & tf_error)
        {
-         if (complain & tf_error)
-           {
-             auto_diagnostic_group d;
-             error ("%qT is not a valid type for a template non-type "
-                    "parameter because it is not structural", type);
-             structural_type_p (type, true);
-           }
-         return true;
+         auto_diagnostic_group d;
+         error ("%qT is not a valid type for a template non-type "
+                "parameter because it is not structural", type);
+         structural_type_p (type, true);
        }
-      return false;
+      return true;
+    }
+  else if (CLASS_TYPE_P (type))
+    {
+      if (complain & tf_error)
+       error ("non-type template parameters of class type only available "
+              "with %<-std=c++20%> or %<-std=gnu++20%>");
+      return true;
     }
 
   if (complain & tf_error)
index 7588c92..9effd27 100644 (file)
@@ -4519,19 +4519,24 @@ zero_init_expr_p (tree t)
 bool
 structural_type_p (tree t, bool explain)
 {
-  t = strip_array_types (t);
-  if (INTEGRAL_OR_ENUMERATION_TYPE_P (t))
-    return true;
-  if (NULLPTR_TYPE_P (t))
-    return true;
-  if (TYPE_PTR_P (t) || TYPE_PTRMEM_P (t))
+  /* A structural type is one of the following: */
+
+  /* a scalar type, or */
+  if (SCALAR_TYPE_P (t))
     return true;
+  /* an lvalue reference type, or */
   if (TYPE_REF_P (t) && !TYPE_REF_IS_RVALUE (t))
     return true;
+  /* a literal class type with the following properties:
+     - all base classes and non-static data members are public and non-mutable
+       and
+     - the types of all bases classes and non-static data members are
+       structural types or (possibly multi-dimensional) array thereof.  */
   if (!CLASS_TYPE_P (t))
     return false;
   if (TREE_CODE (t) == UNION_TYPE)
     {
+      /* FIXME allow (and mangle) unions.  */
       if (explain)
        inform (location_of (t), "%qT is a union", t);
       return false;
@@ -4542,12 +4547,6 @@ structural_type_p (tree t, bool explain)
        explain_non_literal_class (t);
       return false;
     }
-  if (CLASSTYPE_HAS_MUTABLE (t))
-    {
-      if (explain)
-       inform (location_of (t), "%qT has a mutable member", t);
-      return false;
-    }
   for (tree m = next_initializable_field (TYPE_FIELDS (t)); m;
        m = next_initializable_field (DECL_CHAIN (m)))
     {
@@ -4563,12 +4562,19 @@ structural_type_p (tree t, bool explain)
            }
          return false;
        }
-      if (!structural_type_p (TREE_TYPE (m)))
+      if (DECL_MUTABLE_P (m))
+       {
+         if (explain)
+           inform (location_of (m), "%qD is mutable", m);
+         return false;
+       }
+      tree mtype = strip_array_types (TREE_TYPE (m));
+      if (!structural_type_p (mtype))
        {
          if (explain)
            {
              inform (location_of (m), "%qD has a non-structural type", m);
-             structural_type_p (TREE_TYPE (m), true);
+             structural_type_p (mtype, true);
            }
          return false;
        }
index e51e0b9..01260e1 100644 (file)
@@ -4,7 +4,7 @@ namespace N
 { 
   template < typename T > class A
   { 
-    template < T > friend class B;  // { dg-error "not a valid type" }
+    template < T > friend class B;  // { dg-error "not a valid type" "" { target c++17_down } }
   };
 
   A < float > a;
index b86380b..656aacd 100644 (file)
@@ -2,7 +2,7 @@
 template <class... Types> class A
 {
 public:
-  template <Types... Values> class X { /* ... */ }; // { dg-error "not a valid type for a template non-type parameter" }
+  template <Types... Values> class X { /* ... */ }; // { dg-error "not a valid type for a template non-type parameter" "" { target c++17_down } }
 };
 
 template<class... Types> class B
index b1aed43..3ffdbcf 100644 (file)
@@ -4,7 +4,7 @@
 template<auto n> struct B { decltype(n) f = n; };
 B<5> b1;   // OK: template parameter type is int
 B<'a'> b2; // OK: template parameter type is char
-B<2.5> b3; // { dg-error "" } template parameter type cannot be double
+B<2.5> b3; // { dg-error "" "" { target c++17_down } } template parameter type cannot be double
 
 template <auto n> void f(B<n>) { }
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
new file mode 100644 (file)
index 0000000..038d46f
--- /dev/null
@@ -0,0 +1,25 @@
+// { dg-do compile { target c++20 } }
+
+template <auto N> struct A {};
+template <class,class> struct assert_same;
+template <class T> struct assert_same<T,T> {};
+
+#define TEQ(X,Y) static_assert(__is_same(A<(X)>,A<(Y)>))
+#define TNEQ(X,Y) static_assert(!__is_same(A<(X)>,A<(Y)>))
+
+union U {
+  int i; int j;
+  constexpr U(int i): i(i) {}
+  constexpr U(unsigned u): j(u) {}
+};
+
+TEQ(U(0),U(0));
+
+// Calling the other constructor initializes a different member with the same
+// value.  We need to distinguish these.
+TNEQ(U(0),U(0u));
+
+// { dg-final { scan-assembler "_Z1f1AIXtl1Udi1iLi0EEEE" } }
+void f(A<U(0)>) { }
+// { dg-final { scan-assembler "_Z1g1AIXtl1Udi1jLi0EEEE" } }
+void g(A<U(0u)>) { }
index 8d97269..f904bd4 100644 (file)
@@ -4,11 +4,9 @@ typedef double T;
 
 struct A
 {
-  template<T> void foo(); // { dg-error "type" }
+  template<T> void foo(); // { dg-error "type" "" { target c++17_down } }
 };
 
-template<T N = 0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type|declared" }
+template<T N = 0.0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type|declared" "" { target c++17_down } }
 
-B<> b; // { dg-message "non-type" }
-
-// { dg-prune-output "(could not convert|no matches)" }
+B<> b; // { dg-error "(could not convert|no matches)" "" { target c++17_down } }
index 95d80a8..3911327 100644 (file)
@@ -1,6 +1,6 @@
 // PR c++/59115
 
-template<typename T, float, int, typename U> void foo(T, U) {} // { dg-error "valid type" }
+template<typename T, float, int, typename U> void foo(T, U) {} // { dg-error "valid type" "" { target c++17_down } }
 
 void bar()
 {
index 4ec22ef..e37cf8f 100644 (file)
@@ -3,19 +3,19 @@
 
 template<typename T> struct A
 {
-  template<T> int foo();                        // { dg-error "double" }
-  template<template<T> class> int bar();        // { dg-error "double" }
-  template<T> struct X;                         // { dg-error "double" }
+  template<T> int foo();                        // { dg-error "double" "" { target c++17_down } }
+  template<template<T> class> int bar();        // { dg-error "double" "" { target c++17_down } }
+  template<T> struct X;                         // { dg-error "double" "" { target c++17_down } }
 };
 
 A<char>   a1;
-A<double> a2;                                   // { dg-message "required" }
+A<double> a2;                                   // { dg-message "required" "" { target c++17_down } }
 
 template<typename T> struct B
 {
-  template<double> int foo();                   // { dg-error "double" }
-  template<template<double> class> int bar();   // { dg-error "double" }
-  template<double> struct X;                    // { dg-error "double" }
+  template<double> int foo();                   // { dg-error "double" "" { target c++17_down } }
+  template<template<double> class> int bar();   // { dg-error "double" "" { target c++17_down } }
+  template<double> struct X;                    // { dg-error "double" "" { target c++17_down } }
 };
 
 template<void> int foo();                       // { dg-error "void" }
@@ -24,12 +24,12 @@ template<void> struct X;                        // { dg-error "void" }
 
 template<typename T> struct C
 {
-  template<T> int foo();                        // { dg-error "double" }
+  template<T> int foo();                        // { dg-error "double" "" { target c++17_down } }
 };
 
-template<typename T> int baz(T) { C<T> c; return 0;}  // { dg-message "required" }
+template<typename T> int baz(T) { C<T> c; return 0;}  // { dg-message "required" "" { target c++17_down } }
 
 void foobar()
 {
-  baz(1.2);                                     // { dg-message "required" }
+  baz(1.2);                                     // { dg-message "required" "" { target c++17_down } }
 }
index 1124e44..6526a2a 100644 (file)
@@ -1,5 +1,5 @@
 //PR c++/28637
 
 template<void> struct A {};  // { dg-error "not a valid type" }
-A<0> a;                      // { dg-message "non-type" }
+A<0> a;
 
index 5edff9e..2c464b3 100644 (file)
@@ -5,4 +5,4 @@ template<void> struct A         // { dg-error "not a valid type" }
   static int i;
 };
 
-A<0> a;                        // { dg-message "non-type" }
+A<0> a;
index 319a684..37aafd0 100644 (file)
@@ -1,4 +1,4 @@
 //PR c++/28738
 
 template<int,void> struct A {};    // { dg-error "not a valid type" }
-template<int N> struct A<N,0> {};  // { dg-message "invalid" }
+template<int N> struct A<N,0> {};