On Wed, Oct 28, 2020 at 02:34:15PM -0400, Jason Merrill via Gcc-patches wrote: > On 10/28/20 2:02 PM, Marek Polacek wrote: > > This patch implements CWG 625 which prohibits using auto in a template > > argument. A few tests used this construction. We could perhaps only > > give an error in C++20, but not in C++17 with -fconcepts. > > We should not give an error with -fconcepts-ts, this was allowed by the > Concepts TS.
Ah, I see. Presumably we should only get the errors on { target c++20 }. > Does just changing !flag_concepts to !flag_concepts_ts work? Almost: one issue is that it would regress the error message for something like using T = auto; for which we have dedicated code in grokdeclarator, which is nicer than just the terse "invalid use." So I think if flag_concepts is on, we should still check in_template_argument_list_p. Since the logic has gotten a bit tricky, I've introduced a new variable, rather than to play hard-to-read games with ?: in the if. Thanks, Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- This patch implements CWG 625 which prohibits using auto in a template argument. A few tests used this construction. Since this usage was allowed by the Concepts TS, we only give an error in C++20. gcc/cp/ChangeLog: DR 625 PR c++/97479 * parser.c (cp_parser_type_id_1): Reject using auto as a template-argument in C++20. gcc/testsuite/ChangeLog: DR 625 PR c++/97479 * g++.dg/concepts/auto1.C: Add dg-error. * g++.dg/concepts/auto3.C: Likewise. * g++.dg/concepts/auto4.C: Likewise. * g++.dg/cpp0x/auto3.C: Update dg-error. * g++.dg/cpp0x/auto9.C: Likewise. * g++.dg/cpp2a/concepts-pr84979-2.C: Likewise. * g++.dg/cpp2a/concepts-pr84979-3.C: Likewise. * g++.dg/cpp2a/concepts-pr84979.C: Likewise. * g++.dg/DRs/dr625.C: New test. --- gcc/cp/parser.c | 15 +++++++++++++-- gcc/testsuite/g++.dg/DRs/dr625.C | 15 +++++++++++++++ gcc/testsuite/g++.dg/concepts/auto1.C | 4 ++-- gcc/testsuite/g++.dg/concepts/auto3.C | 6 +++--- gcc/testsuite/g++.dg/concepts/auto4.C | 2 +- gcc/testsuite/g++.dg/cpp0x/auto3.C | 2 +- gcc/testsuite/g++.dg/cpp0x/auto9.C | 2 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C | 12 ++++++------ gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C | 12 ++++++------ gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C | 2 +- 10 files changed, 49 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/g++.dg/DRs/dr625.C diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 234079559b9..6570b0af889 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -22417,9 +22417,17 @@ cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags, if (!cp_parser_parse_definitely (parser)) abstract_declarator = NULL; + bool auto_typeid_ok = false; + /* The concepts TS allows 'auto' as a type-id. */ + if (flag_concepts_ts) + auto_typeid_ok = !parser->in_type_id_in_expr_p; + /* DR 625 prohibits use of auto as a template-argument. */ + else if (flag_concepts) + auto_typeid_ok = (!parser->in_type_id_in_expr_p + && !parser->in_template_argument_list_p); + if (type_specifier_seq.type - /* The concepts TS allows 'auto' as a type-id. */ - && (!flag_concepts || parser->in_type_id_in_expr_p) + && !auto_typeid_ok /* None of the valid uses of 'auto' in C++14 involve the type-id nonterminal, but it is valid in a trailing-return-type. */ && !(cxx_dialect >= cxx14 && is_trailing_return)) @@ -22446,6 +22454,9 @@ cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags, inform (DECL_SOURCE_LOCATION (tmpl), "%qD declared here", tmpl); } + else if (parser->in_template_argument_list_p) + error_at (loc, "%qT not permitted in template argument", + auto_node); else error_at (loc, "invalid use of %qT", auto_node); return error_mark_node; diff --git a/gcc/testsuite/g++.dg/DRs/dr625.C b/gcc/testsuite/g++.dg/DRs/dr625.C new file mode 100644 index 00000000000..ce30a9258e6 --- /dev/null +++ b/gcc/testsuite/g++.dg/DRs/dr625.C @@ -0,0 +1,15 @@ +// DR 625 - Use of auto as a template-argument +// PR c++/97479 +// { dg-do compile { target c++14 } } + +template<typename> +struct A { }; + +void f(int); + +int main() +{ + A<decltype(auto)> x = A<void>(); // { dg-error "not permitted|invalid|cannot convert" } + A<auto> a = A<void>(); // { dg-error "not permitted|invalid|cannot convert" } + void (*p)(auto); // { dg-error "parameter" } +} diff --git a/gcc/testsuite/g++.dg/concepts/auto1.C b/gcc/testsuite/g++.dg/concepts/auto1.C index e05330610fc..86f3b8e0aec 100644 --- a/gcc/testsuite/g++.dg/concepts/auto1.C +++ b/gcc/testsuite/g++.dg/concepts/auto1.C @@ -7,8 +7,8 @@ A<int, int> a; A<double, float> a2; A<double, double> a22; -A<auto, auto> b = a; -A<auto, auto> b1 = a2; +A<auto, auto> b = a; // { dg-error "" "" { target c++20 } } +A<auto, auto> b1 = a2; // { dg-error "" "" { target c++20 } } template <class T> concept bool C = __is_same_as (T, int); diff --git a/gcc/testsuite/g++.dg/concepts/auto3.C b/gcc/testsuite/g++.dg/concepts/auto3.C index 27a6afa4ed9..7c021856910 100644 --- a/gcc/testsuite/g++.dg/concepts/auto3.C +++ b/gcc/testsuite/g++.dg/concepts/auto3.C @@ -4,10 +4,10 @@ template <class...> class tuple {}; tuple<int> t; -tuple<auto> y = t; +tuple<auto> y = t; // { dg-error "" "" { target c++20 } } tuple<int,double> t2; -tuple<auto...> x = t2; -tuple<auto...> x2 = t; +tuple<auto...> x = t2; // { dg-error "" "" { target c++20 } } +tuple<auto...> x2 = t; // { dg-error "" "" { target c++20 } } tuple<auto> y2 = t2; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/concepts/auto4.C b/gcc/testsuite/g++.dg/concepts/auto4.C index 8bf3fa9b1ce..2b0e774eac1 100644 --- a/gcc/testsuite/g++.dg/concepts/auto4.C +++ b/gcc/testsuite/g++.dg/concepts/auto4.C @@ -4,7 +4,7 @@ template<typename... Ts> struct A {}; -template<typename... Us> A<auto...> foo() { return A{}; } +template<typename... Us> A<auto...> foo() { return A{}; } // { dg-error "" "" { target c++20 } } void bar() { diff --git a/gcc/testsuite/g++.dg/cpp0x/auto3.C b/gcc/testsuite/g++.dg/cpp0x/auto3.C index 709898db39d..2cd0520023d 100644 --- a/gcc/testsuite/g++.dg/cpp0x/auto3.C +++ b/gcc/testsuite/g++.dg/cpp0x/auto3.C @@ -17,7 +17,7 @@ struct A { }; A<int> A1; // CWG issue 625 -A<auto> A2 = A1; // { dg-error "" "" { target { ! concepts } } } +A<auto> A2 = A1; // { dg-error "" } auto foo() { } // { dg-error "auto" "" { target { ! c++14 } } } diff --git a/gcc/testsuite/g++.dg/cpp0x/auto9.C b/gcc/testsuite/g++.dg/cpp0x/auto9.C index a3f9be521d6..0e80c30ef74 100644 --- a/gcc/testsuite/g++.dg/cpp0x/auto9.C +++ b/gcc/testsuite/g++.dg/cpp0x/auto9.C @@ -114,7 +114,7 @@ badthrow2 () throw (auto &) // { dg-error "invalid use of|expected" } template <auto V = 4> struct G {}; // { dg-error "11:parameter" "" { target { ! c++17 } } } template <typename T> struct H { H (); ~H (); }; -H<auto> h; // { dg-error "invalid|initializer" } +H<auto> h; // { dg-error "invalid|initializer|not permitted in template argument" } void qq (auto); // { dg-error "auto" "" { target { ! concepts } } } void qr (auto*); // { dg-error "auto" "" { target { ! concepts } } } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C index ce69a0f8ac5..290aaf83819 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C @@ -4,9 +4,9 @@ template <typename T> void foo1(T& t) { typename T::template C<void> tcv = t; - typename T::template C<auto> u = tcv; - T::template C<auto>::f (tcv, u); // { dg-error "incomplete" } - (typename T::template D<auto> (t)); // { dg-error "invalid" } + typename T::template C<auto> u = tcv; // { dg-error "not permitted" "" { target c++20 } } + T::template C<auto>::f (tcv, u); // { dg-error "incomplete|not permitted" } + (typename T::template D<auto> (t)); // { dg-error "invalid|not permitted" } } struct T1 { @@ -22,9 +22,9 @@ struct T1 { template <typename T> void foo2(T& t) { typename T::template C<void> tcv = t; - typename T::template C<auto> u = tcv; - T::template C<auto>::f (tcv, u); // { dg-error "incomplete" } - T::template D<auto> (t); // { dg-error "invalid" } + typename T::template C<auto> u = tcv; // { dg-error "not permitted" "" { target c++20 } } + T::template C<auto>::f (tcv, u); // { dg-error "incomplete|not permitted" } + T::template D<auto> (t); // { dg-error "invalid|not permitted" } } struct T2 { diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C index 3809c2d3033..d612327b9ae 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C @@ -8,9 +8,9 @@ template <typename T> void foo1(T& t) { typename T::template C<void> tcv = t; - typename T::template C<auto> u = tcv; - T::template C<auto>::f (tcv, u); // { dg-error "incomplete" } - (typename T::template D<auto> (t)); // { dg-error "invalid" } + typename T::template C<auto> u = tcv; // { dg-error "not permitted" "" { target c++20 } } + T::template C<auto>::f (tcv, u); // { dg-error "incomplete|not permitted" } + (typename T::template D<auto> (t)); // { dg-error "invalid|not permitted" } } struct T1 { @@ -26,9 +26,9 @@ struct T1 { template <typename T> void foo2(T& t) { typename T::template C<void> tcv = t; - typename T::template C<auto> u = tcv; - T::template C<auto>::f (tcv, u); // { dg-error "incomplete" } - T::template D<auto> (t); // { dg-error "yields a type" } + typename T::template C<auto> u = tcv; // { dg-error "not permitted" "" { target c++20 } } + T::template C<auto>::f (tcv, u); // { dg-error "incomplete|not permitted" } + T::template D<auto> (t); // { dg-error "yields a type|not permitted" } } struct T2 { diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C index 9bd40df103a..81555eb4554 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C @@ -5,5 +5,5 @@ template<typename> void foo() {} void bar() { - foo<auto>(); // { dg-error "invalid" } + foo<auto>(); // { dg-error "not permitted|invalid|no matching function" } } base-commit: 323dd4255203479d8c456b85513db4f8e0041d04 -- 2.28.0