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)
{
/* 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)
{
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
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;
}
}
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)
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;
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)))
{
}
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;
}
{
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;
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
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>) { }
--- /dev/null
+// { 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)>) { }
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 } }
// 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()
{
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" }
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 } }
}
//PR c++/28637
template<void> struct A {}; // { dg-error "not a valid type" }
-A<0> a; // { dg-message "non-type" }
+A<0> a;
static int i;
};
-A<0> a; // { dg-message "non-type" }
+A<0> a;
//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> {};