diff mbox series

[pushed] c++: Allow floating-point template parms in C++20.

Message ID 20200710123752.2315-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: Allow floating-point template parms in C++20. | expand

Commit Message

Jason Merrill July 10, 2020, 12:37 p.m. UTC
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.

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

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.
---
 gcc/cp/pt.c                                   | 47 ++++++++++---------
 gcc/cp/tree.c                                 | 34 ++++++++------
 gcc/testsuite/g++.dg/cpp0x/pr81246.C          |  2 +-
 gcc/testsuite/g++.dg/cpp0x/variadic74.C       |  2 +-
 gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C    |  2 +-
 .../g++.dg/cpp2a/nontype-class-equiv1.C       | 25 ++++++++++
 gcc/testsuite/g++.dg/template/crash106.C      |  8 ++--
 gcc/testsuite/g++.dg/template/crash119.C      |  2 +-
 gcc/testsuite/g++.dg/template/nontype12.C     | 20 ++++----
 gcc/testsuite/g++.dg/template/void3.C         |  2 +-
 gcc/testsuite/g++.dg/template/void7.C         |  2 +-
 gcc/testsuite/g++.dg/template/void9.C         |  2 +-
 12 files changed, 89 insertions(+), 59 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C


base-commit: e47dfca5aa473e77fdff95d631dc39de87a41eec
diff mbox series

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b6423f7432b..61f22733858 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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)
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 7588c9248dd..9effd27f587 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -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;
 	}
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr81246.C b/gcc/testsuite/g++.dg/cpp0x/pr81246.C
index e51e0b9a6e1..01260e18534 100644
--- a/gcc/testsuite/g++.dg/cpp0x/pr81246.C
+++ b/gcc/testsuite/g++.dg/cpp0x/pr81246.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic74.C b/gcc/testsuite/g++.dg/cpp0x/variadic74.C
index b86380b4e27..656aacdebe4 100644
--- a/gcc/testsuite/g++.dg/cpp0x/variadic74.C
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic74.C
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
index b1aed4392a7..3ffdbcf0e7e 100644
--- a/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
@@ -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
index 00000000000..038d46fdac8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
@@ -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)>) { }
diff --git a/gcc/testsuite/g++.dg/template/crash106.C b/gcc/testsuite/g++.dg/template/crash106.C
index 8d972699900..f904bd4dc9b 100644
--- a/gcc/testsuite/g++.dg/template/crash106.C
+++ b/gcc/testsuite/g++.dg/template/crash106.C
@@ -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 } }
diff --git a/gcc/testsuite/g++.dg/template/crash119.C b/gcc/testsuite/g++.dg/template/crash119.C
index 95d80a8ffc5..3911327ad3a 100644
--- a/gcc/testsuite/g++.dg/template/crash119.C
+++ b/gcc/testsuite/g++.dg/template/crash119.C
@@ -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()
 {
diff --git a/gcc/testsuite/g++.dg/template/nontype12.C b/gcc/testsuite/g++.dg/template/nontype12.C
index 4ec22ef94e3..e37cf8f7646 100644
--- a/gcc/testsuite/g++.dg/template/nontype12.C
+++ b/gcc/testsuite/g++.dg/template/nontype12.C
@@ -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 } }
 }
diff --git a/gcc/testsuite/g++.dg/template/void3.C b/gcc/testsuite/g++.dg/template/void3.C
index 1124e441033..6526a2a47be 100644
--- a/gcc/testsuite/g++.dg/template/void3.C
+++ b/gcc/testsuite/g++.dg/template/void3.C
@@ -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;
 
diff --git a/gcc/testsuite/g++.dg/template/void7.C b/gcc/testsuite/g++.dg/template/void7.C
index 5edff9e8563..2c464b3a055 100644
--- a/gcc/testsuite/g++.dg/template/void7.C
+++ b/gcc/testsuite/g++.dg/template/void7.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/template/void9.C b/gcc/testsuite/g++.dg/template/void9.C
index 319a684fa43..37aafd0b975 100644
--- a/gcc/testsuite/g++.dg/template/void9.C
+++ b/gcc/testsuite/g++.dg/template/void9.C
@@ -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> {};