From f1612b8ae8a60f62cf5456b3357a341550534a7e Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 17 Aug 2020 16:36:33 -0400 Subject: [PATCH] c++: Check satisfaction before non-dep convs. [CWG2369] It's very hard to use concepts to protect a template from hard errors due to unwanted instantiation if constraints aren't checked until after doing all substitution and checking of non-dependent conversions. It was pretty straightforward to insert the satisfaction check into the logic, but I needed to make the 3-parameter version of satisfy_declaration_constraints call push_tinst_level like the 2-parameter version already does. For simplicity, I also made it add any needed outer template arguments from the TEMPLATE_DECL to the args. The testsuite changes are mostly because this change causes unsatisfaction to cause deduction to fail rather than reject the candidate later in overload resolution. gcc/cp/ChangeLog: DR 2369 * cp-tree.h (push_tinst_level, push_tinst_level_loc): Declare. * constraint.cc (satisfy_declaration_constraints): Use add_outermost_template_args and push_tinst_level. * pt.c (add_outermost_template_args): Handle getting a TEMPLATE_DECL as the first argument. (push_tinst_level, push_tinst_level_loc): No longer static. (fn_type_unification): Check satisfaction before non-dependent conversions. gcc/testsuite/ChangeLog: DR 2369 * g++.dg/concepts/diagnostic10.C: Adjust expexcted errors. * g++.dg/concepts/diagnostic13.C: Adjust expexcted errors. * g++.dg/concepts/diagnostic2.C: Adjust expexcted errors. * g++.dg/concepts/diagnostic3.C: Adjust expexcted errors. * g++.dg/concepts/diagnostic4.C: Adjust expexcted errors. * g++.dg/concepts/diagnostic5.C: Adjust expexcted errors. * g++.dg/concepts/diagnostic9.C: Adjust expexcted errors. * g++.dg/concepts/expression2.C: Adjust expexcted errors. * g++.dg/concepts/fn5.C: Adjust expexcted errors. * g++.dg/concepts/placeholder5.C: Adjust expexcted errors. * g++.dg/concepts/pr67595.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts-pr78752-2.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts-pr84140.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts-recursive-sat3.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts-requires18.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts-requires19.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts3.C: Adjust expexcted errors. * g++.dg/cpp2a/concepts-nondep1.C: New test. * g++.dg/cpp2a/concepts-nondep1a.C: New test. --- gcc/cp/constraint.cc | 12 +++++-- gcc/cp/cp-tree.h | 2 ++ gcc/cp/pt.c | 35 +++++++++++++++---- gcc/testsuite/g++.dg/concepts/diagnostic10.C | 2 +- gcc/testsuite/g++.dg/concepts/diagnostic13.C | 2 +- gcc/testsuite/g++.dg/concepts/diagnostic2.C | 2 +- gcc/testsuite/g++.dg/concepts/diagnostic3.C | 4 +-- gcc/testsuite/g++.dg/concepts/diagnostic4.C | 2 +- gcc/testsuite/g++.dg/concepts/diagnostic5.C | 2 +- gcc/testsuite/g++.dg/concepts/diagnostic9.C | 3 +- gcc/testsuite/g++.dg/concepts/expression2.C | 2 +- gcc/testsuite/g++.dg/concepts/fn5.C | 4 +-- gcc/testsuite/g++.dg/concepts/placeholder5.C | 4 +-- gcc/testsuite/g++.dg/concepts/pr67595.C | 2 +- gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C | 19 ++++++++++ .../g++.dg/cpp2a/concepts-nondep1a.C | 20 +++++++++++ .../g++.dg/cpp2a/concepts-pr78752-2.C | 2 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C | 1 - .../g++.dg/cpp2a/concepts-recursive-sat3.C | 2 +- .../g++.dg/cpp2a/concepts-requires18.C | 4 +-- .../g++.dg/cpp2a/concepts-requires19.C | 12 +++---- gcc/testsuite/g++.dg/cpp2a/concepts3.C | 6 ++-- 22 files changed, 106 insertions(+), 38 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 48d52ec5b7a..7a2f3b9fde0 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -2814,16 +2814,22 @@ satisfy_declaration_constraints (tree t, tree args, subst_info info) info.in_decl = t; gcc_assert (TREE_CODE (t) == TEMPLATE_DECL); + + args = add_outermost_template_args (t, args); + + tree result = boolean_true_node; if (tree norm = normalize_template_requirements (t, info.noisy ())) { + if (!push_tinst_level (t, args)) + return result; tree pattern = DECL_TEMPLATE_RESULT (t); push_access_scope (pattern); - tree result = satisfy_associated_constraints (norm, args, info); + result = satisfy_associated_constraints (norm, args, info); pop_access_scope (pattern); - return result; + pop_tinst_level (); } - return boolean_true_node; + return result; } static tree diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 44531cd86dc..3f3717a6bb5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6981,7 +6981,9 @@ extern bool template_parm_object_p (const_tree); extern tree tparm_object_argument (tree); extern bool explicit_class_specialization_p (tree); extern bool push_tinst_level (tree); +extern bool push_tinst_level (tree, tree); extern bool push_tinst_level_loc (tree, location_t); +extern bool push_tinst_level_loc (tree, tree, location_t); extern void pop_tinst_level (void); extern struct tinst_level *outermost_tinst_level(void); extern void init_template_processing (void); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index edaefcf505f..5dbdd37f6e3 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -586,13 +586,23 @@ add_to_template_args (tree args, tree extra_args) (EXTRA_ARGS) levels are added. This function is used to combine the template arguments from a partial instantiation with the template arguments used to attain the full instantiation from the - partial instantiation. */ + partial instantiation. + + If ARGS is a TEMPLATE_DECL, use its parameters as args. */ tree add_outermost_template_args (tree args, tree extra_args) { tree new_args; + if (!args) + return extra_args; + if (TREE_CODE (args) == TEMPLATE_DECL) + { + tree ti = get_template_info (DECL_TEMPLATE_RESULT (args)); + args = TI_ARGS (ti); + } + /* If there are more levels of EXTRA_ARGS than there are ARGS, something very fishy is going on. */ gcc_assert (TMPL_ARGS_DEPTH (args) >= TMPL_ARGS_DEPTH (extra_args)); @@ -10772,7 +10782,7 @@ static GTY(()) struct tinst_level *last_error_tinst_level; /* We're starting to instantiate D; record the template instantiation context at LOC for diagnostics and to restore it later. */ -static bool +bool push_tinst_level_loc (tree tldcl, tree targs, location_t loc) { struct tinst_level *new_level; @@ -10826,7 +10836,7 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc) /* We're starting substitution of TMPL; record the template substitution context for diagnostics and to restore it later. */ -static bool +bool push_tinst_level (tree tmpl, tree args) { return push_tinst_level_loc (tmpl, args, input_location); @@ -21297,13 +21307,24 @@ fn_type_unification (tree fn, goto fail; } + deduced: + + /* CWG2369: Check satisfaction before non-deducible conversions. */ + if (!constraints_satisfied_p (fn, targs)) + { + if (explain_p) + diagnose_constraints (DECL_SOURCE_LOCATION (fn), fn, targs); + goto fail; + } + /* DR 1391: All parameters have args, now check non-dependent parms for - convertibility. */ - if (check_non_deducible_conversions (parms, args, nargs, fn, strict, flags, - convs, explain_p)) + convertibility. We don't do this if all args were explicitly specified, + as the standard says that we substitute explicit args immediately. */ + if (incomplete + && check_non_deducible_conversions (parms, args, nargs, fn, strict, flags, + convs, explain_p)) goto fail; - deduced: /* All is well so far. Now, check: [temp.deduct] diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic10.C b/gcc/testsuite/g++.dg/concepts/diagnostic10.C index fcc6043ca93..7a90b836143 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic10.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic10.C @@ -14,5 +14,5 @@ struct S void bar() { - foo(); // { dg-error "unsatisfied constraints" } + foo(); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic13.C b/gcc/testsuite/g++.dg/concepts/diagnostic13.C index accd8a6d2bd..1aff859f0bb 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic13.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic13.C @@ -10,5 +10,5 @@ void foo() { } void bar() { - foo(); // { dg-error "unsatisfied constraints" } + foo(); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C index 47accb8366e..004c7a378e7 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic2.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C @@ -22,7 +22,7 @@ template void baz() { - bar(); // { dg-error "unsatisfied constraints" } + bar(); // { dg-error "no match" } /* { dg-begin-multiline-output "" } bar(); ^ diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic3.C b/gcc/testsuite/g++.dg/concepts/diagnostic3.C index b4c75409b94..7796e264251 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic3.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic3.C @@ -24,6 +24,6 @@ baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." } void baz() { - bar(); // { dg-error "unsatisfied constraints" } - baz<2,3,4>(); // { dg-error "unsatisfied constraints" } + bar(); // { dg-error "no match" } + baz<2,3,4>(); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic4.C b/gcc/testsuite/g++.dg/concepts/diagnostic4.C index 677bc867634..e38e35a4d00 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic4.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic4.C @@ -15,4 +15,4 @@ template // { dg-message "typename remove_reference::type" "" { target *-*-* } .-1 } void foo() { } -void bar() { foo (); } // { dg-error "use of" } +void bar() { foo (); } // { dg-error "no match" } diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic5.C b/gcc/testsuite/g++.dg/concepts/diagnostic5.C index 81705f6a0c6..70426ea3099 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic5.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic5.C @@ -39,5 +39,5 @@ template void bar() { - foo(); // { dg-error "use of" } + foo(); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic9.C b/gcc/testsuite/g++.dg/concepts/diagnostic9.C index 414b924f115..8eecfe6f81d 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic9.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic9.C @@ -1,11 +1,12 @@ // PR c++/85278 // { dg-do compile { target concepts } } +// { dg-message "candidate: .*const decltype\\(f2::a\\)&&" "" { target *-*-* } .+2 } template void f2(T a) requires requires (const decltype(a) &&x) { -x; } { } int main() { - f2(nullptr); // { dg-error "use of function .*const decltype\\(f2::a\\)&&" } + f2(nullptr); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/concepts/expression2.C b/gcc/testsuite/g++.dg/concepts/expression2.C index 60dd5414461..4bb5bc71462 100644 --- a/gcc/testsuite/g++.dg/concepts/expression2.C +++ b/gcc/testsuite/g++.dg/concepts/expression2.C @@ -31,7 +31,7 @@ class S int main() { - f1(s); // { dg-error "unsatisfied|private" } + f1(s); // { dg-error "no match" } f2(s); // { dg-error "" } // When used in non-SFINAE contexts, make sure that we fail diff --git a/gcc/testsuite/g++.dg/concepts/fn5.C b/gcc/testsuite/g++.dg/concepts/fn5.C index 6d86ac5d0af..bf277135e79 100644 --- a/gcc/testsuite/g++.dg/concepts/fn5.C +++ b/gcc/testsuite/g++.dg/concepts/fn5.C @@ -19,6 +19,6 @@ int main() { S1 s1; // { dg-error "constraint|invalid" } S2 s2; // { dg-error "constraint|invalid" } - f('a'); // { dg-error "unsatisfied" } - g(0, 'a'); // { dg-error "unsatisfied" } + f('a'); // { dg-error "no match" } + g(0, 'a'); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/concepts/placeholder5.C b/gcc/testsuite/g++.dg/concepts/placeholder5.C index eccad65a301..3f29c933973 100644 --- a/gcc/testsuite/g++.dg/concepts/placeholder5.C +++ b/gcc/testsuite/g++.dg/concepts/placeholder5.C @@ -14,5 +14,5 @@ concept bool C = template constexpr bool f() { return true; } -static_assert(f(), ""); // { dg-error "unsatisfied|as type" } -static_assert(f(), ""); // { dg-error "unsatisfied|as type" } +static_assert(f(), ""); // { dg-error "no match" } +static_assert(f(), ""); // { dg-error "no match" } diff --git a/gcc/testsuite/g++.dg/concepts/pr67595.C b/gcc/testsuite/g++.dg/concepts/pr67595.C index 029ec7a1186..37adf931fb5 100644 --- a/gcc/testsuite/g++.dg/concepts/pr67595.C +++ b/gcc/testsuite/g++.dg/concepts/pr67595.C @@ -5,7 +5,7 @@ template concept bool allocatable = requires{{new X}->X *; }; template concept bool semiregular = allocatable; template concept bool readable = requires{requires semiregular;}; template int weak_input_iterator = requires{{0}->readable;}; -template bool input_iterator{weak_input_iterator}; // { dg-warning "narrowing conversion" } +template bool input_iterator{weak_input_iterator}; // { dg-prune-output "narrowing conversion" } template bool forward_iterator{input_iterator}; template bool bidirectional_iterator{forward_iterator}; template diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C new file mode 100644 index 00000000000..5304b661d2a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C @@ -0,0 +1,19 @@ +// DR 2369 +// { dg-do compile { target c++20 } } + +template struct Z { + typedef typename T::x xx; +}; +template concept C = requires { typename T::A; }; +template typename Z::xx f(void *, T); // #1 +template void f(int, T); // #2 +struct A {} a; +struct ZZ { + template ::xx> operator T *(); + operator int(); +}; +int main() { + ZZ zz; + f(1, a); // OK, deduction fails for #1 because there is no conversion from int to void* + f(zz, 42); // OK, deduction fails for #1 because C is not satisfied +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C new file mode 100644 index 00000000000..2ba4dbc84d6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C @@ -0,0 +1,20 @@ +// DR 2369 +// { dg-do compile { target c++20 } } + +template struct Z { + typedef typename T::x xx; +}; +template concept C = requires { typename T::A; }; // { dg-message "T::A" } +template typename Z::xx f(void *, T); // { dg-message "not satisfied" } + +struct A {} a; +struct ZZ { + template ::xx> operator T *(); + operator int(); +}; +int main() { + ZZ zz; + f(1, a); // { dg-error "no match" } no conversion from int to void* + // { dg-message "cannot convert" "" { target *-*-* } .-1 } + f(zz, 42); // { dg-error "no match" } C is not satisfied +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752-2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752-2.C index 6777054285d..7d7c716cfec 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752-2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752-2.C @@ -17,5 +17,5 @@ int main() func(1, 2, 3); t.func(1, 2, ""); // { dg-error "no match" } - func(1, 2, ""); // { dg-error "unsatisfied constraints" } + func(1, 2, ""); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C index b604f7e00bf..7eff65bee9e 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C @@ -34,4 +34,3 @@ int main() { return distance(I{}, I{}); } - diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat3.C index 679f5cfbf69..b2a6a796ea7 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat3.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat3.C @@ -8,5 +8,5 @@ void foo(T t) { } void test() { - foo(0); // { dg-error "unsatisfied constraints" } + foo(0); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C index 9d8ec94f696..a9b7720cc6c 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C @@ -67,11 +67,11 @@ void test() { f1(); f1(); - f1(); // { dg-error "unsatisfied" } + f1(); // { dg-error "no match" } f2(); f2(); - f2(); // { dg-error "unsatisfied" } + f2(); // { dg-error "no match" } data x; x.f1(); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires19.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires19.C index b020250da50..68aeccf3197 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires19.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires19.C @@ -47,12 +47,12 @@ void f6(T x) requires requires { requires check_v; } { } void test() { - f1(); // { dg-error "unsatisfied" } - f2(0); // { dg-error "unsatisfied" } + f1(); // { dg-error "no match" } + f2(0); // { dg-error "no match" } - f3(); // { dg-error "unsatisfied" } - f4(0); // { dg-error "unsatisfied" } + f3(); // { dg-error "no match" } + f4(0); // { dg-error "no match" } - f5(); // { dg-error "unsatisfied" } - f6(0); // { dg-error "unsatisfied" } + f5(); // { dg-error "no match" } + f6(0); // { dg-error "no match" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts3.C b/gcc/testsuite/g++.dg/cpp2a/concepts3.C index 4ccfd0805b7..1e28e31ad88 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts3.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts3.C @@ -42,7 +42,7 @@ template void f3() { } void driver2() { - f1(); // { dg-error "unsatisfied|is private" } - f2(); // { dg-error "unsatisfied|is private" } - f3(); // { dg-error "unsatisfied|is private" } + f1(); // { dg-error "no match" } + f2(); // { dg-error "no match" } + f3(); // { dg-error "no match" } } -- 2.30.2