mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-23 19:03:59 +08:00
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:
parent
e47dfca5aa
commit
50f071d999
47
gcc/cp/pt.c
47
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,
|
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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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>) { }
|
||||||
|
|
||||||
|
25
gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
Normal file
25
gcc/testsuite/g++.dg/cpp2a/nontype-class-equiv1.C
Normal 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)>) { }
|
@ -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)" }
|
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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 } }
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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> {};
|
||||||
|
Loading…
Reference in New Issue
Block a user