From: Jason Merrill Date: Wed, 9 Nov 2016 20:02:50 +0000 (-0500) Subject: Implement P0127R2, Declaring non-type parameters with auto. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4a826ca6feb3c7ec3ef6287214a3f2820222c97e;p=gcc.git Implement P0127R2, Declaring non-type parameters with auto. gcc/cp/ * cp-tree.h (enum auto_deduction_context): Add adc_unify. * decl.c (grokdeclarator): Allow 'auto' in C++17 template non-type parameter types. * pt.c (do_auto_deduction): Add outer_targs parameter. (convert_template_argument): Call do_auto_deduction. If adc_unify, don't give up on dependent init. (unify): Likewise. In C++17, walk into the type of a TEMPLATE_PARM_INDEX. (for_each_template_parm): Add any_fn parameter. (struct pair_fn_data): Likewise. (for_each_template_parm_r): Call it for any tree. In C++17, walk into the type of a TEMPLATE_PARM_INDEX. (zero_r, array_deduction_r, try_array_deduction): New. (type_unification_real): Call try_array_deduction. (get_partial_spec_bindings): Likewise. gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_template_auto. From-SVN: r242017 --- diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 89b3043ba48..55c2e609606 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,7 @@ +2016-11-09 Jason Merrill + + * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_template_auto. + 2016-11-09 Jakub Jelinek * c-ubsan.c (ubsan_instrument_shift): Handle split diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 55dbf44d34f..70eade170e6 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -942,6 +942,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_aggregate_bases=201603"); cpp_define (pfile, "__cpp_deduction_guides=201606"); cpp_define (pfile, "__cpp_noexcept_function_type=201510"); + cpp_define (pfile, "__cpp_template_auto=201606"); } if (flag_concepts) cpp_define (pfile, "__cpp_concepts=201507"); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index f325ccc8932..e220c5f8562 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,22 @@ +2016-11-09 Jason Merrill + + Implement P0127R2, Declaring non-type parameters with auto. + * cp-tree.h (enum auto_deduction_context): Add adc_unify. + * decl.c (grokdeclarator): Allow 'auto' in C++17 template non-type + parameter types. + * pt.c (do_auto_deduction): Add outer_targs parameter. + (convert_template_argument): Call do_auto_deduction. If adc_unify, + don't give up on dependent init. + (unify): Likewise. In C++17, walk into the type of a + TEMPLATE_PARM_INDEX. + (for_each_template_parm): Add any_fn parameter. + (struct pair_fn_data): Likewise. + (for_each_template_parm_r): Call it for any tree. In C++17, walk + into the type of a TEMPLATE_PARM_INDEX. + (zero_r, array_deduction_r, try_array_deduction): New. + (type_unification_real): Call try_array_deduction. + (get_partial_spec_bindings): Likewise. + 2016-11-07 Jason Merrill Implement P0012R1, Make exception specifications part of the type diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 20b52ad675d..9b5b5bc9bdb 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5163,6 +5163,7 @@ enum auto_deduction_context adc_unspecified, /* Not given */ adc_variable_type, /* Variable initializer deduction */ adc_return_type, /* Return type deduction */ + adc_unify, /* Template argument deduction */ adc_requirement /* Argument dedution constraint */ }; @@ -6088,7 +6089,8 @@ extern tree make_template_placeholder (tree); extern tree do_auto_deduction (tree, tree, tree); extern tree do_auto_deduction (tree, tree, tree, tsubst_flags_t, - auto_deduction_context); + auto_deduction_context, + tree = NULL_TREE); extern tree type_uses_auto (tree); extern tree type_uses_auto_or_concept (tree); extern void append_type_to_template_for_access_check (tree, tree, tree, diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index c0321f9f959..bd37faa8bfd 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -11135,7 +11135,8 @@ grokdeclarator (const cp_declarator *declarator, if (ctype || in_namespace) error ("cannot use %<::%> in parameter declaration"); - if (type_uses_auto (type)) + if (type_uses_auto (type) + && !(cxx_dialect >= cxx1z && template_parm_flag)) { if (cxx_dialect >= cxx14) error ("% parameter not permitted in this context"); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 3df71dd97db..64e566e12b0 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -161,7 +161,7 @@ static tree convert_nontype_argument (tree, tree, tsubst_flags_t); static tree convert_template_argument (tree, tree, tree, tsubst_flags_t, int, tree); static tree for_each_template_parm (tree, tree_fn_t, void*, - hash_set *, bool); + hash_set *, bool, tree_fn_t = NULL); static tree expand_template_argument_pack (tree); static tree build_template_parm_index (int, int, int, tree, tree); static bool inline_needs_template_parms (tree, bool); @@ -7299,6 +7299,13 @@ convert_template_argument (tree parm, { tree t = tsubst (TREE_TYPE (parm), args, complain, in_decl); + if (tree a = type_uses_auto (t)) + { + t = do_auto_deduction (t, arg, a, complain, adc_unspecified); + if (t == error_mark_node) + return error_mark_node; + } + if (invalid_nontype_parm_type_p (t, complain)) return error_mark_node; @@ -8789,6 +8796,7 @@ lookup_and_finish_template_variable (tree templ, tree targs, struct pair_fn_data { tree_fn_t fn; + tree_fn_t any_fn; void *data; /* True when we should also visit template parameters that occur in non-deduced contexts. */ @@ -8811,11 +8819,15 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) do \ { \ result = for_each_template_parm (NODE, fn, data, pfd->visited, \ - pfd->include_nondeduced_p); \ + pfd->include_nondeduced_p, \ + pfd->any_fn); \ if (result) goto out; \ } \ while (0) + if (pfd->any_fn && (*pfd->any_fn)(t, data)) + return t; + if (TYPE_P (t) && (pfd->include_nondeduced_p || TREE_CODE (t) != TYPENAME_TYPE)) WALK_SUBTREE (TYPE_CONTEXT (t)); @@ -8880,7 +8892,8 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) if (pfd->include_nondeduced_p && for_each_template_parm (TYPE_VALUES_RAW (t), fn, data, pfd->visited, - pfd->include_nondeduced_p)) + pfd->include_nondeduced_p, + pfd->any_fn)) return error_mark_node; break; @@ -8911,6 +8924,12 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) return t; else if (!fn) return t; + + /* In C++17 we can deduce a type argument from the type of a non-type + argument. */ + if (cxx_dialect >= cxx1z + && TREE_CODE (t) == TEMPLATE_PARM_INDEX) + WALK_SUBTREE (TREE_TYPE (t)); break; case TEMPLATE_DECL: @@ -8984,13 +9003,15 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) static tree for_each_template_parm (tree t, tree_fn_t fn, void* data, hash_set *visited, - bool include_nondeduced_p) + bool include_nondeduced_p, + tree_fn_t any_fn) { struct pair_fn_data pfd; tree result; /* Set up. */ pfd.fn = fn; + pfd.any_fn = any_fn; pfd.data = data; pfd.include_nondeduced_p = include_nondeduced_p; @@ -18559,6 +18580,53 @@ unify_one_argument (tree tparms, tree targs, tree parm, tree arg, return unify (tparms, targs, parm, arg, arg_strict, explain_p); } +/* for_each_template_parm callback that always returns 0. */ + +static int +zero_r (tree, void *) +{ + return 0; +} + +/* for_each_template_parm any_fn callback to handle deduction of a template + type argument from the type of an array bound. */ + +static int +array_deduction_r (tree t, void *data) +{ + tree_pair_p d = (tree_pair_p)data; + tree &tparms = d->purpose; + tree &targs = d->value; + + if (TREE_CODE (t) == ARRAY_TYPE) + if (tree dom = TYPE_DOMAIN (t)) + if (tree max = TYPE_MAX_VALUE (dom)) + { + if (TREE_CODE (max) == MINUS_EXPR) + max = TREE_OPERAND (max, 0); + if (TREE_CODE (max) == TEMPLATE_PARM_INDEX) + unify (tparms, targs, TREE_TYPE (max), size_type_node, + UNIFY_ALLOW_NONE, /*explain*/false); + } + + /* Keep walking. */ + return 0; +} + +/* Try to deduce any not-yet-deduced template type arguments from the type of + an array bound. This is handled separately from unify because 14.8.2.5 says + "The type of a type parameter is only deduced from an array bound if it is + not otherwise deduced." */ + +static void +try_array_deduction (tree tparms, tree targs, tree parm) +{ + tree_pair_s data = { tparms, targs }; + hash_set visited; + for_each_template_parm (parm, zero_r, &data, &visited, + /*nondeduced*/false, array_deduction_r); +} + /* Most parms like fn_type_unification. If SUBR is 1, we're being called recursively (to unify the @@ -18688,6 +18756,7 @@ type_unification_real (tree tparms, tsubst_flags_t complain = (explain_p ? tf_warning_or_error : tf_none); + bool tried_array_deduction = (cxx_dialect < cxx1z); for (i = 0; i < ntparms; i++) { @@ -18706,6 +18775,15 @@ type_unification_real (tree tparms, continue; tparm = TREE_VALUE (tparm); + if (TREE_CODE (tparm) == TYPE_DECL + && !tried_array_deduction) + { + try_array_deduction (tparms, targs, xparms); + tried_array_deduction = true; + if (TREE_VEC_ELT (targs, i)) + continue; + } + /* If this is an undeduced nontype parameter that depends on a type parameter, try another pass; its type may have been deduced from a later argument than the one from which @@ -19378,8 +19456,8 @@ template_parm_level_and_index (tree parm, int* level, int* index) /* Unifies the remaining arguments in PACKED_ARGS with the pack expansion at the end of PACKED_PARMS. Returns 0 if the type deduction succeeds, 1 otherwise. STRICT is the same as in - unify. CALL_ARGS_P is true iff PACKED_ARGS is actually a function - call argument list. We'll need to adjust the arguments to make them + fn_type_unification. CALL_ARGS_P is true iff PACKED_ARGS is actually a + function call argument list. We'll need to adjust the arguments to make them types. SUBR tells us if this is from a recursive call to type_unification_real, or for comparing two template argument lists. */ @@ -19680,6 +19758,9 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, tree targ; tree tparm; int strict_in = strict; + tsubst_flags_t complain = (explain_p + ? tf_warning_or_error + : tf_none); /* I don't think this will do the right thing with respect to types. But the only case I've seen it in so far has been array bounds, where @@ -19897,9 +19978,7 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, if (coerce_template_parms (parm_parms, full_argvec, TYPE_TI_TEMPLATE (parm), - (explain_p - ? tf_warning_or_error - : tf_none), + complain, /*require_all_args=*/true, /*use_default_args=*/false) == error_mark_node) @@ -20046,6 +20125,18 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, return x; } + if (cxx_dialect >= cxx1z + /* We deduce from array bounds in try_array_deduction. */ + && !(strict & UNIFY_ALLOW_INTEGER) + && uses_template_parms (TREE_TYPE (parm)) + && !type_uses_auto (TREE_TYPE (parm))) + { + tree atype = TREE_TYPE (arg); + RECUR_AND_CHECK_FAILURE (tparms, targs, + TREE_TYPE (parm), atype, + UNIFY_ALLOW_NONE, explain_p); + } + /* [temp.deduct.type] If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function @@ -20055,6 +20146,13 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, deduced from an array bound may be of any integral type. The non-type parameter might use already deduced type parameters. */ tparm = tsubst (TREE_TYPE (parm), targs, 0, NULL_TREE); + if (tree a = type_uses_auto (tparm)) + { + tparm = do_auto_deduction (tparm, arg, a, complain, adc_unify); + if (tparm == error_mark_node) + return 1; + } + if (!TREE_TYPE (arg)) /* Template-parameter dependent expression. Just accept it for now. It will later be processed in convert_template_argument. */ @@ -21015,6 +21113,8 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args) else deduced_args = innermost_deduced_args; + bool tried_array_deduction = (cxx_dialect < cxx1z); + again: if (unify (tparms, deduced_args, INNERMOST_TEMPLATE_ARGS (spec_args), INNERMOST_TEMPLATE_ARGS (args), @@ -21023,7 +21123,17 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args) for (i = 0; i < ntparms; ++i) if (! TREE_VEC_ELT (innermost_deduced_args, i)) - return NULL_TREE; + { + if (!tried_array_deduction) + { + try_array_deduction (tparms, innermost_deduced_args, + INNERMOST_TEMPLATE_ARGS (spec_args)); + tried_array_deduction = true; + if (TREE_VEC_ELT (innermost_deduced_args, i)) + goto again; + } + return NULL_TREE; + } tree tinst = build_tree_list (spec_tmpl, deduced_args); if (!push_tinst_level (tinst)) @@ -24607,14 +24717,16 @@ do_auto_deduction (tree type, tree init, tree auto_node) tree do_auto_deduction (tree type, tree init, tree auto_node, - tsubst_flags_t complain, auto_deduction_context context) + tsubst_flags_t complain, auto_deduction_context context, + tree outer_targs) { tree targs; if (init == error_mark_node) return error_mark_node; - if (type_dependent_expression_p (init)) + if (type_dependent_expression_p (init) + && context != adc_unify) /* Defining a subset of type-dependent expressions that we can deduce from ahead of time isn't worth the trouble. */ return type; @@ -24733,6 +24845,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, switch (context) { case adc_unspecified: + case adc_unify: error("placeholder constraints not satisfied"); break; case adc_variable_type: @@ -24754,8 +24867,9 @@ do_auto_deduction (tree type, tree init, tree auto_node, } } - if (processing_template_decl) - targs = add_to_template_args (current_template_args (), targs); + if (processing_template_decl && context != adc_unify) + outer_targs = current_template_args (); + targs = add_to_template_args (outer_targs, targs); return tsubst (type, targs, complain, NULL_TREE); } diff --git a/gcc/testsuite/g++.dg/cpp0x/auto9.C b/gcc/testsuite/g++.dg/cpp0x/auto9.C index 9001f78fa45..771ce0ef9d3 100644 --- a/gcc/testsuite/g++.dg/cpp0x/auto9.C +++ b/gcc/testsuite/g++.dg/cpp0x/auto9.C @@ -111,7 +111,7 @@ badthrow2 () throw (auto &) // { dg-error "invalid use of|expected" } { } -template struct G {}; // { dg-error "auto" } +template struct G {}; // { dg-error "auto" "" { target { ! c++1z } } } template struct H { H (); ~H (); }; H h; // { dg-error "invalid|initializer" } diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C index f4658a96067..adbc32cf4ce 100644 --- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C +++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C @@ -368,6 +368,12 @@ # error "__cpp_aligned_new != 201606" #endif +#ifndef __cpp_template_auto +# error "__cpp_template_auto" +#elif __cpp_template_auto != 201606 +# error "__cpp_template_auto != 201606" +#endif + #ifndef __cpp_inline_variables # error "__cpp_inline_variables" #elif __cpp_inline_variables != 201606 diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C new file mode 100644 index 00000000000..9d0507483be --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C @@ -0,0 +1,13 @@ +// Testcase from P0127R2 +// { dg-options -std=c++1z } + +template struct A { }; + +template struct C; +template struct C> +{ + using Q = T; +}; + +typedef long R; +typedef C>::Q R; // OK; T was deduced to long from the template argument value in the type A<2> diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C new file mode 100644 index 00000000000..23dac8a9151 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C @@ -0,0 +1,10 @@ +// Testcase from P0127R2 +// { dg-options -std=c++1z } + +template struct S; +template struct S { + using Q = T; +}; + +typedef S::Q V; +typedef decltype(sizeof 0) V; // OK; T was deduced to std::size_t from the type int[42] diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C new file mode 100644 index 00000000000..00b56b1fe77 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C @@ -0,0 +1,15 @@ +// Testcase from P0127R2 +// { dg-options -std=c++1z } + +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 + +template void f(B) { } + +int main() +{ + f(B<42>()); + f(B<'a'>()); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C new file mode 100644 index 00000000000..80bbbed9565 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C @@ -0,0 +1,14 @@ +// { dg-options -std=c++1z } + +template void f(T, int (&)[n]); +template void g(int (&)[n], T); +template void h(int (&)[n]); + +int main() +{ + const int i = 42; + int ar[i]; + h(ar); + f(i, ar); + g(ar, i); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C new file mode 100644 index 00000000000..aa5ca7f0d2c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C @@ -0,0 +1,15 @@ +// { dg-options -std=c++1z } + +template struct A +{ + template struct Y; + template struct Y

{ using type1 = decltype (p); }; + template struct Y { using type2 = decltype (pp); }; +}; + +int i; +int *p; + +A::Y<&i>::type1 t1; +A::Y<&p>::type2 t2; + diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C new file mode 100644 index 00000000000..cbf1b46a73a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C @@ -0,0 +1,8 @@ +// { dg-do compile { target c++11 } } + +template struct A; +template int foo(A *) = delete; +void foo(void *); +void bar(A<0> *p) { + foo(p); // { dg-error "" "" { target c++1z } } +} diff --git a/gcc/testsuite/g++.dg/template/partial5.C b/gcc/testsuite/g++.dg/template/partial5.C index 979e4c62bf6..2f400f7d44c 100644 --- a/gcc/testsuite/g++.dg/template/partial5.C +++ b/gcc/testsuite/g++.dg/template/partial5.C @@ -14,7 +14,7 @@ template struct Y { }; template -struct Y { }; // { dg-error "not deducible|U" } +struct Y { }; // { dg-error "not deducible|U" "" { target { ! c++1z } } } template