On Apr 3, 2018, Jason Merrill <ja...@redhat.com> wrote: > On Tue, Apr 3, 2018 at 3:54 AM, Alexandre Oliva <aol...@redhat.com> wrote: >> On Apr 2, 2018, Jason Merrill <ja...@redhat.com> wrote: >> >>> On Sat, Mar 31, 2018 at 4:23 AM, Alexandre Oliva <aol...@redhat.com> wrote: >>>> On Mar 30, 2018, Jason Merrill <ja...@redhat.com> wrote: >>>> template <typename T> >>>> void foo(T t) { >>>> typename T::template C<auto> u = t; >>>> T::template C<auto> (t); >>>> T::template C<auto>::f (t, u); >>>> } >> >>> We should be able to distinguish those cases based on tag_type.
>> [...] >> And then, while we're parsing "template C<auto>", we haven't yet reached >> the '::' after the closing angle bracket that would tell us to regard >> the id necessarily as a typename, so I don't see how we'd get a >> scope_type in tag_type for the third case. > Ah, good point. Then perhaps put the new function in pt.c and also > call it from tsubst_copy_and_build? I couldn't figure out how to trigger checks in tsubst_copy_and_build; the testcases I threw at it all went through tsubst_qualified_id. I think we can only get an identifier_p in a TEMPLATE_ID_EXPR when it's a qualified id naming a member of another template; unqualified names that could possibly take explicit template arguments would have to have visible declarations, and then we'd get (an overload of) the declarations in the TEMPLATE_ID_EXPR. I see evidence in pt.c that some cases of Koenig lookup may involve identifier_p template substitution, and I see evidence in the standard that the unqualified template-id could have explicit template args, but... we won't regard <whatever> as a template arg list if it's not preceded by an id that names a template, be it a template-dependent qualified id preceded by the 'template' keyword, be a template found by name lookup. If we find an unrelated template function (as in the snippet below), we'll stick to it. So it seems to me that the patch below is all we need. Am I missing anything? struct foo { template <typename T> friend void bar(T& t); }; template <int> void bar(); template <typename T> void f(T& x) { bar<auto> (x); } void g(foo& x) { f (x); } Regstrapping on i686- and x86_64-linux-gnu. Ok to install if it passes? [PR c++/84979] reject auto in explicit tmpl args for tmpl-fn With concepts, we accept auto in explicit template arguments, but we should only accept them for template classes. Passing them to template functions or variables is not allowed. So, reject it, at parse time if possible, at specialization time otherwise. for gcc/cp/ChangeLog PR c++/84979 * pt.c (check_auto_in_tmpl_args): New. (tsubst_qualified_id): Use it to reject template args referencing auto for non-type templates. * parser.c (cp_parser_template_id): Likewise. * cp-tree.h (check_auto_in_tmpl_args): Declare. * typeck2.c (build_functional_cast): Report correct location for invalid use of auto. for gcc/testsuite/ChangeLog PR c++/84979 * g++.dg/concepts/pr84979.C: New. * g++.dg/concepts/pr84979-2.C: New. * g++.dg/concepts/pr84979-3.C: New. --- gcc/cp/cp-tree.h | 1 + gcc/cp/parser.c | 10 +++++- gcc/cp/pt.c | 52 +++++++++++++++++++++++++++++ gcc/cp/typeck2.c | 3 +- gcc/testsuite/g++.dg/concepts/pr84979-2.C | 41 +++++++++++++++++++++++ gcc/testsuite/g++.dg/concepts/pr84979-3.C | 45 +++++++++++++++++++++++++ gcc/testsuite/g++.dg/concepts/pr84979.C | 9 +++++ 7 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/concepts/pr84979-2.C create mode 100644 gcc/testsuite/g++.dg/concepts/pr84979-3.C create mode 100644 gcc/testsuite/g++.dg/concepts/pr84979.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index db79338035da..4777aa34cdbe 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6496,6 +6496,7 @@ extern void maybe_show_extern_c_location (void); /* in pt.c */ extern bool check_template_shadow (tree); +extern bool check_auto_in_tmpl_args (tree, tree); extern tree get_innermost_template_args (tree, int); extern void maybe_begin_member_template_processing (tree); extern void maybe_end_member_template_processing (void); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index e946d0b72292..40a1e5549f02 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -15831,8 +15831,16 @@ cp_parser_template_id (cp_parser *parser, location_t combined_loc = make_location (token->location, token->location, finish_loc); + /* Check for concepts autos where they don't belong. We could + identify types in some cases of idnetifier TEMPL, looking ahead + for a CPP_SCOPE, but that would buy us nothing: we accept auto in + types. We reject them in functions, but if what we have is an + identifier, even with none_type we can't conclude it's NOT a + type, we have to wait for template substitution. */ + if (flag_concepts && check_auto_in_tmpl_args (templ, arguments)) + template_id = error_mark_node; /* Build a representation of the specialization. */ - if (identifier_p (templ)) + else if (identifier_p (templ)) template_id = build_min_nt_loc (combined_loc, TEMPLATE_ID_EXPR, templ, arguments); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 284eaf3cab66..a5c489fc0768 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -14890,6 +14890,15 @@ tsubst_qualified_id (tree qualified_id, tree args, complain); else expr = lookup_template_function (expr, template_args); + + /* We may be repeating a check already done during parsing, but + if it was well-formed and passed then, it will pass again + now, and if it didn't, we wouldn't have got here. The case + we want to catch is when we couldn't tell then, and can now, + namely when templ prior to substitution was an + identifier. */ + if (flag_concepts && check_auto_in_tmpl_args (expr, template_args)) + return error_mark_node; } if (expr == error_mark_node && complain & tf_error) @@ -26500,6 +26509,49 @@ type_uses_auto (tree type) return find_type_usage (type, is_auto); } +/* Report ill-formed occurrences of auto types in ARGUMENTS. If + concepts are enabled, auto is acceptable in template arguments, but + only when TEMPL identifies a template class. Return TRUE if any + such errors were reported. */ + +bool +check_auto_in_tmpl_args (tree tmpl, tree args) +{ + /* If there were previous errors, nevermind. */ + if (!args || TREE_CODE (args) != TREE_VEC) + return false; + + /* If TMPL is an identifier, we're parsing and we can't tell yet + whether TMPL is supposed to be a type, a function or a variable. + We'll only be able to tell during template substitution, so we + expect to be called again then. If concepts are enabled and we + know we have a type, we're ok. */ + if (flag_concepts + && (identifier_p (tmpl) + || (DECL_P (tmpl) + && (DECL_TYPE_TEMPLATE_P (tmpl) + || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))))) + return false; + + /* Quickly search for any occurrences of auto; usually there won't + be any, and then we'll avoid allocating the vector. */ + if (!type_uses_auto (args)) + return false; + + bool errors = false; + + tree vec = extract_autos (args); + for (int i = 0; i < TREE_VEC_LENGTH (vec); i++) + { + tree xauto = TREE_VALUE (TREE_VEC_ELT (vec, i)); + error_at (DECL_SOURCE_LOCATION (xauto), + "invalid use of %qT in template argument", xauto); + errors = true; + } + + return errors; +} + /* For a given template T, return the vector of typedefs referenced in T for which access check is needed at T instantiation time. T is either a FUNCTION_DECL or a RECORD_TYPE. diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 3aae0a362d5d..3bdeae1501f5 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -2081,7 +2081,8 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain) if (!CLASS_PLACEHOLDER_TEMPLATE (anode)) { if (complain & tf_error) - error ("invalid use of %qT", anode); + error_at (DECL_SOURCE_LOCATION (TEMPLATE_TYPE_DECL (anode)), + "invalid use of %qT", anode); return error_mark_node; } else if (!parms) diff --git a/gcc/testsuite/g++.dg/concepts/pr84979-2.C b/gcc/testsuite/g++.dg/concepts/pr84979-2.C new file mode 100644 index 000000000000..ce69a0f8ac53 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/pr84979-2.C @@ -0,0 +1,41 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fconcepts" } + +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" } +} + +struct T1 { + template <typename T> struct C { + C(T1&); + static void f(T1&, C&); + }; + template <typename T> struct D { + D(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" } +} + +struct T2 { + template <typename T> struct C { + C(T2&); + static void f(T2&, C&); + }; + template <typename T> static void D(T2&); +}; + +void f(T1& t1, T2& t2) { + foo1 (t1); + foo2 (t2); +} diff --git a/gcc/testsuite/g++.dg/concepts/pr84979-3.C b/gcc/testsuite/g++.dg/concepts/pr84979-3.C new file mode 100644 index 000000000000..3809c2d3033b --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/pr84979-3.C @@ -0,0 +1,45 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fconcepts" } + +// This is like pr84979-2.C, except that we swap the types passed to +// the template functions foo1 and foo2, so that the expectations WRT +// D's typeness are not met. + +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" } +} + +struct T1 { + template <typename T> struct C { + C(T1&); + static void f(T1&, C&); + }; + template <typename T> struct D { + D(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" } +} + +struct T2 { + template <typename T> struct C { + C(T2&); + static void f(T2&, C&); + }; + template <typename T> static void D(T2&); +}; + +void f(T1& t1, T2& t2) { + foo1 (t2); + foo2 (t1); +} diff --git a/gcc/testsuite/g++.dg/concepts/pr84979.C b/gcc/testsuite/g++.dg/concepts/pr84979.C new file mode 100644 index 000000000000..9bd40df103a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/pr84979.C @@ -0,0 +1,9 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fconcepts" } + +template<typename> void foo() {} + +void bar() +{ + foo<auto>(); // { dg-error "invalid" } +} -- Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ You must be the change you wish to see in the world. -- Gandhi Be Free! -- http://FSFLA.org/ FSF Latin America board member Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer