c++: Diagnose self-recursive satisfaction
This patch further extends the satisfaction_cache class to diagnose self-recursive satisfaction. gcc/cp/ChangeLog: * constraint.cc (sat_entry::evaluating): New member. (satisfaction_cache::get): If entry->evaluating, diagnose self-recursive satisfaction. Otherwise, set entry->evaluating if we're not reusing a cached satisfaction result. (satisfaction_cache::save): Clear entry->evaluating. (satisfy_atom): Set up diagnosing_failed_constraint before the first call to get(). gcc/testsuite/ChangeLog: PR c++/96840 * g++.dg/cpp2a/concepts-pr88395.C: Adjust to expect the self-recursive satisfaction to get directly diagnosed. * g++.dg/cpp2a/concepts-recursive-sat2.C: Likewise. * g++.dg/cpp2a/concepts-recursive-sat4.C: New test.
This commit is contained in:
parent
20f292863f
commit
79f57d5cb0
4 changed files with 49 additions and 17 deletions
|
@ -2428,6 +2428,11 @@ struct GTY((for_user)) sat_entry
|
|||
We don't always want to do so, in order to avoid emitting duplicate
|
||||
diagnostics in some cases. */
|
||||
bool diagnose_instability;
|
||||
|
||||
/* True if we're in the middle of computing this satisfaction result.
|
||||
Used during both quiet and noisy satisfaction to detect self-recursive
|
||||
satisfaction. */
|
||||
bool evaluating;
|
||||
};
|
||||
|
||||
struct sat_hasher : ggc_ptr_hash<sat_entry>
|
||||
|
@ -2572,6 +2577,7 @@ satisfaction_cache
|
|||
mapping, we set this flag (in satisfy_atom) only if substitution
|
||||
into its mapping previously failed. */
|
||||
entry->diagnose_instability = true;
|
||||
entry->evaluating = false;
|
||||
*slot = entry;
|
||||
}
|
||||
else
|
||||
|
@ -2590,9 +2596,23 @@ satisfaction_cache::get ()
|
|||
if (!entry)
|
||||
return NULL_TREE;
|
||||
|
||||
if (info.noisy () || entry->maybe_unstable)
|
||||
/* We're recomputing the satisfaction result from scratch. */
|
||||
return NULL_TREE;
|
||||
if (entry->evaluating)
|
||||
{
|
||||
/* If we get here, it means satisfaction is self-recursive. */
|
||||
gcc_checking_assert (!entry->result);
|
||||
if (info.noisy ())
|
||||
error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)),
|
||||
"satisfaction of atomic constraint %qE depends on itself",
|
||||
entry->atom);
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
if (info.noisy () || entry->maybe_unstable || !entry->result)
|
||||
{
|
||||
/* We're computing the satisfaction result from scratch. */
|
||||
entry->evaluating = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
else
|
||||
return entry->result;
|
||||
}
|
||||
|
@ -2607,6 +2627,9 @@ satisfaction_cache::save (tree result)
|
|||
if (!entry)
|
||||
return result;
|
||||
|
||||
gcc_checking_assert (entry->evaluating);
|
||||
entry->evaluating = false;
|
||||
|
||||
if (entry->result && result != entry->result)
|
||||
{
|
||||
if (info.quiet ())
|
||||
|
@ -2856,6 +2879,11 @@ static void diagnose_atomic_constraint (tree, tree, tree, subst_info);
|
|||
static tree
|
||||
satisfy_atom (tree t, tree args, sat_info info)
|
||||
{
|
||||
/* In case there is a diagnostic, we want to establish the context
|
||||
prior to printing errors. If no errors occur, this context is
|
||||
removed before returning. */
|
||||
diagnosing_failed_constraint failure (t, args, info.noisy ());
|
||||
|
||||
satisfaction_cache cache (t, args, info);
|
||||
if (tree r = cache.get ())
|
||||
return r;
|
||||
|
@ -2863,11 +2891,6 @@ satisfy_atom (tree t, tree args, sat_info info)
|
|||
/* Perform substitution quietly. */
|
||||
subst_info quiet (tf_none, NULL_TREE);
|
||||
|
||||
/* In case there is a diagnostic, we want to establish the context
|
||||
prior to printing errors. If no errors occur, this context is
|
||||
removed before returning. */
|
||||
diagnosing_failed_constraint failure (t, args, info.noisy ());
|
||||
|
||||
/* Instantiate the parameter mapping. */
|
||||
tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet);
|
||||
if (map == error_mark_node)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template <class T, class U>
|
||||
concept Concept2 = requires (T t, U u)
|
||||
concept Concept2 = requires (T t, U u) // { dg-error "depends on itself" }
|
||||
{
|
||||
t += u; // { dg-error "template instantiation depth" }
|
||||
t += u;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
|
@ -17,7 +17,5 @@ struct S
|
|||
|
||||
constexpr S operator * (S a, S b)
|
||||
{
|
||||
return a += b;
|
||||
return a += b; // { dg-error "no match" }
|
||||
}
|
||||
|
||||
// { dg-prune-output "compilation terminated" }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template<typename T>
|
||||
concept Fooable = requires(T t) { foo(t); }; // { dg-error "template instantiation depth" }
|
||||
concept Fooable = requires(T t) { foo(t); }; // { dg-error "depends on itself" }
|
||||
|
||||
template<Fooable T>
|
||||
void foo(T t) { }
|
||||
|
@ -9,7 +9,5 @@ void foo(T t) { }
|
|||
void test()
|
||||
{
|
||||
struct S {} s;
|
||||
foo(s);
|
||||
foo(s); // { dg-error "no match" }
|
||||
}
|
||||
|
||||
// { dg-prune-output "compilation terminated" }
|
||||
|
|
13
gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C
Normal file
13
gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C
Normal file
|
@ -0,0 +1,13 @@
|
|||
// PR c++/96840
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template <class T, class U> concept C = requires(T t, U u) { t * u; };
|
||||
// { dg-message "required for the satisfaction of 'C<T, Rep>' .with T = Int<int>; Rep = int." "" { target *-*-* } .-1 }
|
||||
// { dg-error "depends on itself" "" { target *-*-* } .-2 }
|
||||
|
||||
template <class Rep> struct Int {
|
||||
template <class T> requires C<T, Rep> friend void operator*(T, Int) { }
|
||||
template <class T> requires C<T, Rep> friend void operator*(Int, T) { }
|
||||
};
|
||||
|
||||
void f() { 0 * Int<int>{}; }
|
Loading…
Add table
Reference in a new issue