From 50f071d999374b6dd13149a31f505d4457c9de6d Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 2 Jul 2020 15:14:52 -0400 Subject: [PATCH] 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. --- 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 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 A { public: - template class X { /* ... */ }; // { dg-error "not a valid type for a template non-type parameter" } + template class X { /* ... */ }; // { dg-error "not a valid type for a template non-type parameter" "" { target c++17_down } } }; template 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 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 void f(B) { } 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 struct A {}; +template struct assert_same; +template struct assert_same {}; + +#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) { } +// { dg-final { scan-assembler "_Z1g1AIXtl1Udi1jLi0EEEE" } } +void g(A) { } 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 void foo(); // { dg-error "type" } + template void foo(); // { dg-error "type" "" { target c++17_down } } }; -template > struct B {}; // { dg-error "type|declared" } +template > 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 void foo(T, U) {} // { dg-error "valid type" } +template 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 struct A { - template int foo(); // { dg-error "double" } - template class> int bar(); // { dg-error "double" } - template struct X; // { dg-error "double" } + template int foo(); // { dg-error "double" "" { target c++17_down } } + template class> int bar(); // { dg-error "double" "" { target c++17_down } } + template struct X; // { dg-error "double" "" { target c++17_down } } }; A a1; -A a2; // { dg-message "required" } +A a2; // { dg-message "required" "" { target c++17_down } } template struct B { - template int foo(); // { dg-error "double" } - template class> int bar(); // { dg-error "double" } - template struct X; // { dg-error "double" } + template int foo(); // { dg-error "double" "" { target c++17_down } } + template class> int bar(); // { dg-error "double" "" { target c++17_down } } + template struct X; // { dg-error "double" "" { target c++17_down } } }; template int foo(); // { dg-error "void" } @@ -24,12 +24,12 @@ template struct X; // { dg-error "void" } template struct C { - template int foo(); // { dg-error "double" } + template int foo(); // { dg-error "double" "" { target c++17_down } } }; -template int baz(T) { C c; return 0;} // { dg-message "required" } +template int baz(T) { C 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 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 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 struct A {}; // { dg-error "not a valid type" } -template struct A {}; // { dg-message "invalid" } +template struct A {}; -- 2.30.2