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,
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)

View 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;
}

View 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;

View 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

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

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

View 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()
{

View 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 } }
}

View 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;

View 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;

View 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> {};