mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 20:01:22 -05:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
35
gcc/cp/pt.c
35
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<ARGS>; 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]
|
||||
|
||||
@@ -14,5 +14,5 @@ struct S
|
||||
void
|
||||
bar()
|
||||
{
|
||||
foo<S>(); // { dg-error "unsatisfied constraints" }
|
||||
foo<S>(); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -10,5 +10,5 @@ void foo() { }
|
||||
|
||||
void bar()
|
||||
{
|
||||
foo<int, char>(); // { dg-error "unsatisfied constraints" }
|
||||
foo<int, char>(); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ template<typename T>
|
||||
void
|
||||
baz()
|
||||
{
|
||||
bar<int>(); // { dg-error "unsatisfied constraints" }
|
||||
bar<int>(); // { dg-error "no match" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
bar<int>();
|
||||
^
|
||||
|
||||
@@ -24,6 +24,6 @@ baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." }
|
||||
void
|
||||
baz()
|
||||
{
|
||||
bar<int, char>(); // { dg-error "unsatisfied constraints" }
|
||||
baz<2,3,4>(); // { dg-error "unsatisfied constraints" }
|
||||
bar<int, char>(); // { dg-error "no match" }
|
||||
baz<2,3,4>(); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@ template<typename T>
|
||||
// { dg-message "typename remove_reference<T>::type" "" { target *-*-* } .-1 }
|
||||
void foo() { }
|
||||
|
||||
void bar() { foo<int> (); } // { dg-error "use of" }
|
||||
void bar() { foo<int> (); } // { dg-error "no match" }
|
||||
|
||||
@@ -39,5 +39,5 @@ template<typename T>
|
||||
void
|
||||
bar()
|
||||
{
|
||||
foo<char>(); // { dg-error "use of" }
|
||||
foo<char>(); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// PR c++/85278
|
||||
// { dg-do compile { target concepts } }
|
||||
|
||||
// { dg-message "candidate: .*const decltype\\(f2::a\\)&&" "" { target *-*-* } .+2 }
|
||||
template<typename T>
|
||||
void f2(T a)
|
||||
requires requires (const decltype(a) &&x) { -x; }
|
||||
{ }
|
||||
|
||||
int main() {
|
||||
f2<void*>(nullptr); // { dg-error "use of function .*const decltype\\(f2::a\\)&&" }
|
||||
f2<void*>(nullptr); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,6 +19,6 @@ int main() {
|
||||
S1<char> s1; // { dg-error "constraint|invalid" }
|
||||
S2<int, char> 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" }
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@ concept bool C =
|
||||
template <C c>
|
||||
constexpr bool f() { return true; }
|
||||
|
||||
static_assert(f<double>(), ""); // { dg-error "unsatisfied|as type" }
|
||||
static_assert(f<int>(), ""); // { dg-error "unsatisfied|as type" }
|
||||
static_assert(f<double>(), ""); // { dg-error "no match" }
|
||||
static_assert(f<int>(), ""); // { dg-error "no match" }
|
||||
|
||||
@@ -5,7 +5,7 @@ template <class X> concept bool allocatable = requires{{new X}->X *; };
|
||||
template <class X> concept bool semiregular = allocatable<X>;
|
||||
template <class X> concept bool readable = requires{requires semiregular<X>;};
|
||||
template <class> int weak_input_iterator = requires{{0}->readable;};
|
||||
template <class X> bool input_iterator{weak_input_iterator<X>}; // { dg-warning "narrowing conversion" }
|
||||
template <class X> bool input_iterator{weak_input_iterator<X>}; // { dg-prune-output "narrowing conversion" }
|
||||
template <class X> bool forward_iterator{input_iterator<X>};
|
||||
template <class X> bool bidirectional_iterator{forward_iterator<X>};
|
||||
template <class X>
|
||||
|
||||
19
gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C
Normal file
19
gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// DR 2369
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template <class T> struct Z {
|
||||
typedef typename T::x xx;
|
||||
};
|
||||
template <class T> concept C = requires { typename T::A; };
|
||||
template <C T> typename Z<T>::xx f(void *, T); // #1
|
||||
template <class T> void f(int, T); // #2
|
||||
struct A {} a;
|
||||
struct ZZ {
|
||||
template <class T, class = typename Z<T>::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<int> is not satisfied
|
||||
}
|
||||
20
gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C
Normal file
20
gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C
Normal file
@@ -0,0 +1,20 @@
|
||||
// DR 2369
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template <class T> struct Z {
|
||||
typedef typename T::x xx;
|
||||
};
|
||||
template <class T> concept C = requires { typename T::A; }; // { dg-message "T::A" }
|
||||
template <C T> typename Z<T>::xx f(void *, T); // { dg-message "not satisfied" }
|
||||
|
||||
struct A {} a;
|
||||
struct ZZ {
|
||||
template <class T, class = typename Z<T>::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<int> is not satisfied
|
||||
}
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
@@ -34,4 +34,3 @@ int main()
|
||||
{
|
||||
return distance(I<int>{}, I<void>{});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ void foo(T t) { }
|
||||
|
||||
void test()
|
||||
{
|
||||
foo(0); // { dg-error "unsatisfied constraints" }
|
||||
foo(0); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -67,11 +67,11 @@ void test()
|
||||
{
|
||||
f1<int>();
|
||||
f1<bool>();
|
||||
f1<void>(); // { dg-error "unsatisfied" }
|
||||
f1<void>(); // { dg-error "no match" }
|
||||
|
||||
f2<int>();
|
||||
f2<bool>();
|
||||
f2<void>(); // { dg-error "unsatisfied" }
|
||||
f2<void>(); // { dg-error "no match" }
|
||||
|
||||
data<char> x;
|
||||
x.f1<int>();
|
||||
|
||||
@@ -47,12 +47,12 @@ void f6(T x) requires requires { requires check_v<decltype(x)>; } { }
|
||||
|
||||
void test()
|
||||
{
|
||||
f1<int>(); // { dg-error "unsatisfied" }
|
||||
f2(0); // { dg-error "unsatisfied" }
|
||||
f1<int>(); // { dg-error "no match" }
|
||||
f2(0); // { dg-error "no match" }
|
||||
|
||||
f3<int>(); // { dg-error "unsatisfied" }
|
||||
f4(0); // { dg-error "unsatisfied" }
|
||||
f3<int>(); // { dg-error "no match" }
|
||||
f4(0); // { dg-error "no match" }
|
||||
|
||||
f5<int>(); // { dg-error "unsatisfied" }
|
||||
f6(0); // { dg-error "unsatisfied" }
|
||||
f5<int>(); // { dg-error "no match" }
|
||||
f6(0); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ template<typename T>
|
||||
void f3() { }
|
||||
|
||||
void driver2() {
|
||||
f1<S1>(); // { dg-error "unsatisfied|is private" }
|
||||
f2<S1>(); // { dg-error "unsatisfied|is private" }
|
||||
f3<S1>(); // { dg-error "unsatisfied|is private" }
|
||||
f1<S1>(); // { dg-error "no match" }
|
||||
f2<S1>(); // { dg-error "no match" }
|
||||
f3<S1>(); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user