From 79f57d5cb070bb02ea0a34b5f42658d6659b19a8 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Thu, 17 Dec 2020 22:18:07 -0500 Subject: [PATCH] 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. --- gcc/cp/constraint.cc | 39 +++++++++++++++---- gcc/testsuite/g++.dg/cpp2a/concepts-pr88395.C | 8 ++-- .../g++.dg/cpp2a/concepts-recursive-sat2.C | 6 +-- .../g++.dg/cpp2a/concepts-recursive-sat4.C | 13 +++++++ 4 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 47cf882a704..40e499bbbd1 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -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 @@ -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) diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr88395.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr88395.C index 1c25252d47b..e1792e19005 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr88395.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr88395.C @@ -1,9 +1,9 @@ // { dg-do compile { target c++20 } } template -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 @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C index 992fcbbfccb..9bc96f58979 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C @@ -1,7 +1,7 @@ // { dg-do compile { target c++20 } } template -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 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" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C new file mode 100644 index 00000000000..18d126e05ea --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat4.C @@ -0,0 +1,13 @@ +// PR c++/96840 +// { dg-do compile { target c++20 } } + +template concept C = requires(T t, U u) { t * u; }; +// { dg-message "required for the satisfaction of 'C' .with T = Int; Rep = int." "" { target *-*-* } .-1 } +// { dg-error "depends on itself" "" { target *-*-* } .-2 } + +template struct Int { + template requires C friend void operator*(T, Int) { } + template requires C friend void operator*(Int, T) { } +}; + +void f() { 0 * Int{}; } -- 2.30.2