From 7d119905abdf28f56486be0ca01a42f8efcea1a2 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 12 Nov 2018 23:47:20 -0500 Subject: [PATCH] Avoid double substitution with complete explicit template arguments. Previously, when we got a function template with explicit arguments for all of the template parameters, we still did "deduction", which of course couldn't deduce anything, but did other deduction-time checking of non-dependent conversions and such. This broke down with the unevaluated lambdas patch (to follow): substituting into the lambda multiple times, once to get the function type for deduction and then again to generate the actual decl, doesn't work, since different substitutions of a lambda produce different types. I believe that skipping the initial substitution when we have all the arguments is still conformant, and produces better diagnostics for some testcases. * pt.c (fn_type_unification): If we have a full set of explicit arguments, go straight to substitution. From-SVN: r266055 --- gcc/cp/ChangeLog | 3 + gcc/cp/pt.c | 75 +++++++++++-------- gcc/testsuite/g++.dg/cpp0x/decltype48.C | 2 +- gcc/testsuite/g++.dg/cpp0x/diag1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/error4.C | 2 +- gcc/testsuite/g++.dg/cpp0x/pr77655.C | 2 +- .../g++.dg/diagnostic/param-type-mismatch-2.C | 16 ++-- .../g++.dg/diagnostic/param-type-mismatch.C | 10 +-- 8 files changed, 61 insertions(+), 51 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 79c162c75b0..5cd147114a0 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,8 @@ 2018-11-12 Jason Merrill + * pt.c (fn_type_unification): If we have a full set of explicit + arguments, go straight to substitution. + * decl2.c (min_vis_expr_r, expr_visibility): New. (min_vis_r): Call expr_visibility. (constrain_visibility_for_template): Likewise. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 0c33c8e1527..f948aef3776 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -19800,6 +19800,11 @@ fn_type_unification (tree fn, tsubst_flags_t complain = (explain_p ? tf_warning_or_error : tf_none); bool ok; static int deduction_depth; + /* type_unification_real will pass back any access checks from default + template argument substitution. */ + vec *checks = NULL; + /* We don't have all the template args yet. */ + bool incomplete = true; tree orig_fn = fn; if (flag_new_inheriting_ctors) @@ -19857,7 +19862,7 @@ fn_type_unification (tree fn, template results in an invalid type, type deduction fails. */ int i, len = TREE_VEC_LENGTH (tparms); location_t loc = input_location; - bool incomplete = false; + incomplete = false; if (explicit_targs == error_mark_node) goto fail; @@ -19923,33 +19928,52 @@ fn_type_unification (tree fn, } } - if (!push_tinst_level (fn, explicit_targs)) + if (incomplete) { - excessive_deduction_depth = true; - goto fail; - } - processing_template_decl += incomplete; - input_location = DECL_SOURCE_LOCATION (fn); - /* Ignore any access checks; we'll see them again in - instantiate_template and they might have the wrong - access path at this point. */ - push_deferring_access_checks (dk_deferred); - fntype = tsubst (TREE_TYPE (fn), explicit_targs, - complain | tf_partial | tf_fndecl_type, NULL_TREE); - pop_deferring_access_checks (); - input_location = loc; - processing_template_decl -= incomplete; - pop_tinst_level (); + if (!push_tinst_level (fn, explicit_targs)) + { + excessive_deduction_depth = true; + goto fail; + } + ++processing_template_decl; + input_location = DECL_SOURCE_LOCATION (fn); + /* Ignore any access checks; we'll see them again in + instantiate_template and they might have the wrong + access path at this point. */ + push_deferring_access_checks (dk_deferred); + tsubst_flags_t ecomplain = complain | tf_partial | tf_fndecl_type; + fntype = tsubst (TREE_TYPE (fn), explicit_targs, ecomplain, NULL_TREE); + pop_deferring_access_checks (); + input_location = loc; + --processing_template_decl; + pop_tinst_level (); - if (fntype == error_mark_node) - goto fail; + if (fntype == error_mark_node) + goto fail; + } /* Place the explicitly specified arguments in TARGS. */ explicit_targs = INNERMOST_TEMPLATE_ARGS (explicit_targs); for (i = NUM_TMPL_ARGS (explicit_targs); i--;) TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (explicit_targs, i); + if (!incomplete && CHECKING_P + && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs)) + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT + (targs, NUM_TMPL_ARGS (explicit_targs)); + } + + if (return_type && strict != DEDUCE_CALL) + { + tree *new_args = XALLOCAVEC (tree, nargs + 1); + new_args[0] = return_type; + memcpy (new_args + 1, args, nargs * sizeof (tree)); + args = new_args; + ++nargs; } + if (!incomplete) + goto deduced; + /* Never do unification on the 'this' parameter. */ parms = skip_artificial_parms_for (fn, TYPE_ARG_TYPES (fntype)); @@ -19963,14 +19987,7 @@ fn_type_unification (tree fn, } else if (return_type) { - tree *new_args; - parms = tree_cons (NULL_TREE, TREE_TYPE (fntype), parms); - new_args = XALLOCAVEC (tree, nargs + 1); - new_args[0] = return_type; - memcpy (new_args + 1, args, nargs * sizeof (tree)); - args = new_args; - ++nargs; } /* We allow incomplete unification without an error message here @@ -19988,11 +20005,6 @@ fn_type_unification (tree fn, goto fail; } - /* type_unification_real will pass back any access checks from default - template argument substitution. */ - vec *checks; - checks = NULL; - ok = !type_unification_real (DECL_INNERMOST_TEMPLATE_PARMS (fn), full_targs, parms, args, nargs, /*subr=*/0, strict, &checks, explain_p); @@ -20035,6 +20047,7 @@ fn_type_unification (tree fn, convs, explain_p)) goto fail; + deduced: /* All is well so far. Now, check: [temp.deduct] diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype48.C b/gcc/testsuite/g++.dg/cpp0x/decltype48.C index 39fc4ebf555..7f5005b915c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/decltype48.C +++ b/gcc/testsuite/g++.dg/cpp0x/decltype48.C @@ -6,7 +6,7 @@ template struct baz { }; template T bar(); template -baz(bar() ...))> // { dg-error "no match" } +baz(bar() ...))> // { dg-error "" } foo(); int main() diff --git a/gcc/testsuite/g++.dg/cpp0x/diag1.C b/gcc/testsuite/g++.dg/cpp0x/diag1.C index 9bf7cfe5c8f..1f8c4a44821 100644 --- a/gcc/testsuite/g++.dg/cpp0x/diag1.C +++ b/gcc/testsuite/g++.dg/cpp0x/diag1.C @@ -23,7 +23,7 @@ struct TypeC // TypeC::fn() // we don't want to see the template header, return type, or parameter bindings // for TypeB::fn. - template auto fn() -> decltype(b.fn()); // { dg-bogus "typename|with" } + template auto fn() -> decltype(b.fn()); // { dg-bogus "typename" } }; int main() diff --git a/gcc/testsuite/g++.dg/cpp0x/error4.C b/gcc/testsuite/g++.dg/cpp0x/error4.C index bd73b2a766c..1f365b7a10a 100644 --- a/gcc/testsuite/g++.dg/cpp0x/error4.C +++ b/gcc/testsuite/g++.dg/cpp0x/error4.C @@ -12,7 +12,7 @@ struct S { template static decltype(*declval()) get(...); // { dg-error "operator*" } - typedef decltype(get(declval())) type; // { dg-error "no match" } + typedef decltype(get(declval())) type; // { dg-error "" } }; struct X { }; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr77655.C b/gcc/testsuite/g++.dg/cpp0x/pr77655.C index ab2e942cbb8..8b4ffa66ea6 100644 --- a/gcc/testsuite/g++.dg/cpp0x/pr77655.C +++ b/gcc/testsuite/g++.dg/cpp0x/pr77655.C @@ -3,7 +3,7 @@ template void g(F); template -auto h(A &&... a) -> decltype(g(0, g(a)...)) { // { dg-error "no matching" } +auto h(A &&... a) -> decltype(g(0, g(a)...)) { // { dg-error "" } h([] {}); // { dg-error "no matching" } } diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C index f74f8d39e26..b19655d6496 100644 --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C @@ -134,21 +134,17 @@ int test_7 (int one, T two, float three); // { dg-line test_7_decl } int test_7 (int first, const char *second, float third) { return test_7 (first, second, third); // { dg-line test_7_usage } - // { dg-error "no matching function" "" { target *-*-* } test_7_usage } + // { dg-message "cannot convert 'const char\\*' to 'const char\\*\\*'" "" { target *-*-* } test_7_usage } /* { dg-begin-multiline-output "" } return test_7 (first, second, third); - ^ + ^~~~~~ + | + const char* { dg-end-multiline-output "" } */ - // { dg-message "candidate: 'template int test_7\\(int, T, float\\)'" "" { target *-*-* } test_7_decl } + // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl } /* { dg-begin-multiline-output "" } int test_7 (int one, T two, float three); - ^~~~~~ - { dg-end-multiline-output "" } */ - // { dg-message "template argument deduction/substitution failed:" "" { target *-*-* } test_7_decl } - // { dg-message "cannot convert 'second' \\(type 'const char\\*'\\) to type 'const char\\*\\*'" "" { target *-*-* } test_7_usage } - /* { dg-begin-multiline-output "" } - return test_7 (first, second, third); - ^~~~~~ + ~~^~~ { dg-end-multiline-output "" } */ } diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C index 50bbd4ae94e..cb5c3602a66 100644 --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C @@ -130,18 +130,16 @@ int test_7 (int one, T two, float three); int test_7 (int first, int second, float third) { - return test_7 (first, second, third); // { dg-error "no matching function" } - /* { dg-begin-multiline-output "" } - return test_7 (first, second, third); - ^ - { dg-end-multiline-output "" } */ + return test_7 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" } /* { dg-begin-multiline-output "" } return test_7 (first, second, third); ^~~~~~ + | + int { dg-end-multiline-output "" } */ /* { dg-begin-multiline-output "" } int test_7 (int one, T two, float three); - ^~~~~~ + ~~^~~ { dg-end-multiline-output "" } */ } -- 2.30.2