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

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.
This commit is contained in:
Jason Merrill 2020-07-02 15:14:52 -04:00
parent e47dfca5aa
commit 50f071d999
12 changed files with 89 additions and 59 deletions

View 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, For a non-type template-parameter of integral or enumeration type,
integral promotions (_conv.prom_) and integral conversions integral promotions (_conv.prom_) and integral conversions
(_conv.integral_) are applied. */ (_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) 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 /* Notice that there are constant expressions like '4 % 0' which
do not fold into integer constants. */ 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) if (complain & tf_error)
{ {
@ -7287,7 +7288,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
return NULL_TREE; return NULL_TREE;
/* else cxx_constant_value complained but gave us /* else cxx_constant_value complained but gave us
a real constant, so go ahead. */ a real constant, so go ahead. */
if (TREE_CODE (expr) != INTEGER_CST) if (!CONSTANT_CLASS_P (expr))
{ {
/* Some assemble time constant expressions like /* Some assemble time constant expressions like
(intptr_t)&&lab1 - (intptr_t)&&lab2 or (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. */ compile time. Refuse them here. */
gcc_checking_assert (reduced_constant_expression_p (expr)); gcc_checking_assert (reduced_constant_expression_p (expr));
error_at (loc, "template argument %qE for type %qT not " error_at (loc, "template argument %qE for type %qT not "
"a constant integer", expr, type); "a compile-time constant", expr, type);
return NULL_TREE; return NULL_TREE;
} }
} }
@ -26127,31 +26128,31 @@ invalid_nontype_parm_type_p (tree type, tsubst_flags_t complain)
else if (cxx_dialect >= cxx11 else if (cxx_dialect >= cxx11
&& TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM) && TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM)
return false; 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)) if (dependent_type_p (type))
return false; return false;
if (!complete_type_or_else (type, NULL_TREE)) if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
return true; 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 "
auto_diagnostic_group d; "parameter because it is not structural", type);
error ("%qT is not a valid type for a template non-type " structural_type_p (type, true);
"parameter because it is not structural", type);
structural_type_p (type, true);
}
return 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) if (complain & tf_error)

View File

@ -4519,19 +4519,24 @@ zero_init_expr_p (tree t)
bool bool
structural_type_p (tree t, bool explain) structural_type_p (tree t, bool explain)
{ {
t = strip_array_types (t); /* A structural type is one of the following: */
if (INTEGRAL_OR_ENUMERATION_TYPE_P (t))
return true; /* a scalar type, or */
if (NULLPTR_TYPE_P (t)) if (SCALAR_TYPE_P (t))
return true;
if (TYPE_PTR_P (t) || TYPE_PTRMEM_P (t))
return true; return true;
/* an lvalue reference type, or */
if (TYPE_REF_P (t) && !TYPE_REF_IS_RVALUE (t)) if (TYPE_REF_P (t) && !TYPE_REF_IS_RVALUE (t))
return true; 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)) if (!CLASS_TYPE_P (t))
return false; return false;
if (TREE_CODE (t) == UNION_TYPE) if (TREE_CODE (t) == UNION_TYPE)
{ {
/* FIXME allow (and mangle) unions. */
if (explain) if (explain)
inform (location_of (t), "%qT is a union", t); inform (location_of (t), "%qT is a union", t);
return false; return false;
@ -4542,12 +4547,6 @@ structural_type_p (tree t, bool explain)
explain_non_literal_class (t); explain_non_literal_class (t);
return false; 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; for (tree m = next_initializable_field (TYPE_FIELDS (t)); m;
m = next_initializable_field (DECL_CHAIN (m))) m = next_initializable_field (DECL_CHAIN (m)))
{ {
@ -4563,12 +4562,19 @@ structural_type_p (tree t, bool explain)
} }
return false; 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) if (explain)
{ {
inform (location_of (m), "%qD has a non-structural type", m); 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; return false;
} }

View File

@ -4,7 +4,7 @@ namespace N
{ {
template < typename T > class A 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; A < float > a;

View File

@ -2,7 +2,7 @@
template <class... Types> class A template <class... Types> class A
{ {
public: 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 template<class... Types> class B

View File

@ -4,7 +4,7 @@
template<auto n> struct B { decltype(n) f = n; }; template<auto n> struct B { decltype(n) f = n; };
B<5> b1; // OK: template parameter type is int B<5> b1; // OK: template parameter type is int
B<'a'> b2; // OK: template parameter type is char 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>) { } template <auto n> void f(B<n>) { }

View File

@ -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)>) { }

View File

@ -4,11 +4,9 @@ typedef double T;
struct A 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" } B<> b; // { dg-error "(could not convert|no matches)" "" { target c++17_down } }
// { dg-prune-output "(could not convert|no matches)" }

View File

@ -1,6 +1,6 @@
// PR c++/59115 // 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() void bar()
{ {

View File

@ -3,19 +3,19 @@
template<typename T> struct A template<typename T> struct A
{ {
template<T> int foo(); // { dg-error "double" } template<T> int foo(); // { dg-error "double" "" { target c++17_down } }
template<template<T> class> int bar(); // { dg-error "double" } template<template<T> class> int bar(); // { dg-error "double" "" { target c++17_down } }
template<T> struct X; // { dg-error "double" } template<T> struct X; // { dg-error "double" "" { target c++17_down } }
}; };
A<char> a1; 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<typename T> struct B
{ {
template<double> int foo(); // { dg-error "double" } template<double> int foo(); // { dg-error "double" "" { target c++17_down } }
template<template<double> class> int bar(); // { dg-error "double" } template<template<double> class> int bar(); // { dg-error "double" "" { target c++17_down } }
template<double> struct X; // { dg-error "double" } template<double> struct X; // { dg-error "double" "" { target c++17_down } }
}; };
template<void> int foo(); // { dg-error "void" } template<void> int foo(); // { dg-error "void" }
@ -24,12 +24,12 @@ template<void> struct X; // { dg-error "void" }
template<typename T> struct C 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() void foobar()
{ {
baz(1.2); // { dg-message "required" } baz(1.2); // { dg-message "required" "" { target c++17_down } }
} }

View File

@ -1,5 +1,5 @@
//PR c++/28637 //PR c++/28637
template<void> struct A {}; // { dg-error "not a valid type" } template<void> struct A {}; // { dg-error "not a valid type" }
A<0> a; // { dg-message "non-type" } A<0> a;

View File

@ -5,4 +5,4 @@ template<void> struct A // { dg-error "not a valid type" }
static int i; static int i;
}; };
A<0> a; // { dg-message "non-type" } A<0> a;

View File

@ -1,4 +1,4 @@
//PR c++/28738 //PR c++/28738
template<int,void> struct A {}; // { dg-error "not a valid type" } 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> {};