On Mon, 16 Mar 2020, Patrick Palka wrote:

> Hi Martin,
> 
> On Sun, 15 Mar 2020, Martin Sebor wrote:
> 
> > On 3/11/20 4:18 PM, Patrick Palka via Gcc-patches wrote:
> > ...
> > > Hmm, like this?  This version introduces a new static member function
> > > diagnosing_failed_constraint::replay_errors_p that checks
> > > current_constraint_diagnosis_depth, and also sets max_depth_exceeded_p
> > > when appropriate.
> > > 
> > ...
> > 
> > Just one small quoting nit:
> > 
> > 
> > > @@ -3368,11 +3464,25 @@ diagnose_constraints (location_t loc, tree t, tree
> > > args)
> > >   {
> > >     inform (loc, "constraints not satisfied");
> > >   +  if (concepts_diagnostics_max_depth == 0)
> > > +    return;
> > > +
> > >     /* Replay satisfaction, but diagnose errors.  */
> > >     if (!args)
> > >       constraint_satisfaction_value (t, tf_warning_or_error);
> > >     else
> > >       constraint_satisfaction_value (t, args, tf_warning_or_error);
> > > +
> > > +  static bool suggested_p;
> > > +  if (concepts_diagnostics_max_depth_exceeded_p
> > > +      && current_constraint_diagnosis_depth == 0
> > > +      && !suggested_p)
> > > +    {
> > > +      inform (UNKNOWN_LOCATION,
> > > +       "set -fconcepts-diagnostics-depth= to at least %d for more
> > > detail",
> > > +       concepts_diagnostics_max_depth + 1);
> > 
> > 
> > Can you please quote the option name in the diagnostic (e.g., by using
> > "set %qs to...", "-fconcepts-diagnostics-depth=" or equivalent) to avoid
> > -Wformat-diag?  It won't cause errors (yet) but will eventually need to
> > be cleaned up.
> 
> Yes sure, consider it done.  I've amended the patch locally with this
> change.

Here's a rebased version of this patch with the above diagnostic change,
no other changes made (aside from a minor adjustment to diagnostic5.C):

-- >8 --

gcc/c-family/ChangeLog:

        * c.opt: Add -fconcepts-diagnostics-depth.

gcc/cp/ChangeLog:

        * constraint.cc (finish_constraint_binary_op): Set the location of EXPR
        as well as its range, because build_x_binary_op doesn't always do so.
        (current_constraint_diagnosis_depth): New.
        (concepts_diagnostics_max_depth_exceeded_p): New.
        (collect_operands_of_disjunction): New.
        (satisfy_disjunction): When diagnosing a satisfaction failure, maybe
        replay each branch of the disjunction, subject to the current diagnosis
        depth.
        (diagnose_valid_expression): When diagnosing a satisfaction failure,
        maybe replay the substitution error, subject to the current diagnosis
        recursion.
        (diagnose_valid_type): Likewise.
        (diagnose_nested_requiremnet): Likewise.
        (diagnosing_failed_constraint::diagnosing_failed_constraint): Increment
        current_constraint_diagnosis_depth when diagnosing.
        (diagnosing_failed_constraint::~diagnosing_failed_constraint): Decrement
        current_constraint_diagnosis_depth when diagnosing.
        (diagnosing_failed_constraint::replay_errors_p): New static member
        function.
        (diagnose_constraints): Don't diagnose if concepts_diagnostics_max_depth
        is 0.  Emit a one-off note to increase -fconcepts-diagnostics-depth if
        the limit was exceeded.
        * cp-tree.h (diagnosing_failed_constraint::replay_errors_p): Declare.

gcc/testsuite/ChangeLog:

        * g++.dg/concepts/diagnostic2.C: Expect "no operand" instead of
        "neither operand".
        * g++.dg/concepts/diagnostic5.C: New test.
---
 gcc/c-family/c.opt                          |   4 +
 gcc/cp/constraint.cc                        | 150 +++++++++++++++++---
 gcc/cp/cp-tree.h                            |   1 +
 gcc/testsuite/g++.dg/concepts/diagnostic2.C |   2 +-
 gcc/testsuite/g++.dg/concepts/diagnostic5.C |  46 ++++++
 5 files changed, 182 insertions(+), 21 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic5.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index a1e0f4cdc3a..c49da99d395 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1453,6 +1453,10 @@ fconcepts-ts
 C++ ObjC++ Var(flag_concepts_ts) Init(0)
 Enable certain features present in the Concepts TS.
 
+fconcepts-diagnostics-depth=
+C++ ObjC++ Joined RejectNegative UInteger Var(concepts_diagnostics_max_depth) 
Init(1)
+Specify maximum error replay depth during recursive diagnosis of a constraint 
satisfaction failure.
+
 fcond-mismatch
 C ObjC C++ ObjC++
 Allow the arguments of the '?' operator to have different types.
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index a86bcdf603a..76c520318c3 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -162,6 +162,7 @@ finish_constraint_binary_op (location_t loc,
   /* When either operand is dependent, the overload set may be non-empty.  */
   if (expr == error_mark_node)
     return error_mark_node;
+  expr.set_location (loc);
   expr.set_range (lhs.get_start (), rhs.get_finish ());
   return expr;
 }
@@ -2395,6 +2396,49 @@ satisfy_conjunction (tree t, tree args, subst_info info)
   return satisfy_constraint_r (TREE_OPERAND (t, 1), args, info);
 }
 
+/* The current depth at which we're replaying an error during recursive
+   diagnosis of a constraint satisfaction failure.  */
+
+static int current_constraint_diagnosis_depth;
+
+/* Whether CURRENT_CONSTRAINT_DIAGNOSIS_DEPTH has ever exceeded
+   CONCEPTS_DIAGNOSTICS_MAX_DEPTH during recursive diagnosis of a constraint
+   satisfaction error.  */
+
+static bool concepts_diagnostics_max_depth_exceeded_p;
+
+/* Recursive subroutine of collect_operands_of_disjunction.  T is a normalized
+   subexpression of a constraint (composed of CONJ_CONSTRs and DISJ_CONSTRs)
+   and E is the corresponding unnormalized subexpression (composed of
+   TRUTH_ANDIF_EXPRs and TRUTH_ORIF_EXPRs).  */
+
+static void
+collect_operands_of_disjunction_r (tree t, tree e,
+                                  auto_vec<tree_pair> *operands)
+{
+  if (TREE_CODE (e) == TRUTH_ORIF_EXPR)
+    {
+      collect_operands_of_disjunction_r (TREE_OPERAND (t, 0),
+                                        TREE_OPERAND (e, 0), operands);
+      collect_operands_of_disjunction_r (TREE_OPERAND (t, 1),
+                                        TREE_OPERAND (e, 1), operands);
+    }
+  else
+    {
+      tree_pair p = std::make_pair (t, e);
+      operands->safe_push (p);
+    }
+}
+
+/* Recursively collect the normalized and unnormalized operands of the
+   disjunction T and append them to OPERANDS in order.  */
+
+static void
+collect_operands_of_disjunction (tree t, auto_vec<tree_pair> *operands)
+{
+  collect_operands_of_disjunction_r (t, CONSTR_EXPR (t), operands);
+}
+
 /* Compute the satisfaction of a disjunction.  */
 
 static tree
@@ -2412,11 +2456,25 @@ satisfy_disjunction (tree t, tree args, subst_info info)
   tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet);
   if (rhs != boolean_true_node && info.noisy ())
     {
-      location_t loc = cp_expr_location (CONSTR_EXPR (t));
-      inform (loc, "neither operand of the disjunction is satisfied");
-      /* TODO: Replay the LHS and RHS to find failures in both branches.  */
-      // satisfy_constraint_r (TREE_OPERAND (t, 0), args, info);
-      // satisfy_constraint_r (TREE_OPERAND (t, 1), args, info);
+      cp_expr disj_expr = CONSTR_EXPR (t);
+      inform (disj_expr.get_location (),
+             "no operand of the disjunction is satisfied");
+      if (diagnosing_failed_constraint::replay_errors_p ())
+       {
+         /* Replay the error in each branch of the disjunction.  */
+         auto_vec<tree_pair> operands;
+         collect_operands_of_disjunction (t, &operands);
+         for (unsigned i = 0; i < operands.length (); i++)
+           {
+             tree norm_op = operands[i].first;
+             tree op = operands[i].second;
+             location_t loc = make_location (cp_expr_location (op),
+                                             disj_expr.get_start (),
+                                             disj_expr.get_finish ());
+             inform (loc, "the operand %qE is unsatisfied because", op);
+             satisfy_constraint_r (norm_op, args, info);
+           }
+       }
     }
   return rhs;
 }
@@ -3182,10 +3240,14 @@ diagnose_valid_expression (tree expr, tree args, tree 
in_decl)
     return result;
 
   location_t loc = cp_expr_loc_or_input_loc (expr);
-  inform (loc, "the required expression %qE is invalid", expr);
-
-  /* TODO: Replay the substitution to diagnose the error?  */
-  // tsubst_expr (expr, args, tf_error, in_decl, false);
+  if (diagnosing_failed_constraint::replay_errors_p ())
+    {
+      /* Replay the substitution error.  */
+      inform (loc, "the required expression %qE is invalid, because", expr);
+      tsubst_expr (expr, args, tf_error, in_decl, false);
+    }
+  else
+    inform (loc, "the required expression %qE is invalid", expr);
 
   return error_mark_node;
 }
@@ -3198,10 +3260,14 @@ diagnose_valid_type (tree type, tree args, tree in_decl)
     return result;
 
   location_t loc = cp_expr_loc_or_input_loc (type);
-  inform (loc, "the required type %qT is invalid", type);
-
-  /* TODO: Replay the substitution to diagnose the error?  */
-  // tsubst (type, args, tf_error, in_decl);
+  if (diagnosing_failed_constraint::replay_errors_p ())
+    {
+      /* Replay the substitution error.  */
+      inform (loc, "the required type %qT is invalid, because", type);
+      tsubst (type, args, tf_error, in_decl);
+    }
+  else
+    inform (loc, "the required type %qT is invalid", type);
 
   return error_mark_node;
 }
@@ -3280,11 +3346,16 @@ diagnose_nested_requirement (tree req, tree args)
 
   tree expr = TREE_OPERAND (req, 0);
   location_t loc = cp_expr_location (expr);
-  inform (loc, "nested requirement %qE is not satisfied", expr);
+  if (diagnosing_failed_constraint::replay_errors_p ())
+    {
+      /* Replay the substitution error.  */
+      inform (loc, "nested requirement %qE is not satisfied, because", expr);
+      subst_info noisy (tf_warning_or_error, NULL_TREE);
+      satisfy_constraint_expression (expr, args, noisy);
+    }
+  else
+    inform (loc, "nested requirement %qE is not satisfied", expr);
 
-  /* TODO: Replay the substitution to diagnose the error?  */
-  // subst_info noisy (tf_warning_or_error, NULL_TREE);
-  // satisfy_constraint (norm, args, info);
 }
 
 static void
@@ -3385,14 +3456,38 @@ diagnosing_failed_constraint (tree t, tree args, bool 
diag)
   : diagnosing_error (diag)
 {
   if (diagnosing_error)
-    current_failed_constraint = tree_cons (args, t, current_failed_constraint);
+    {
+      current_failed_constraint
+       = tree_cons (args, t, current_failed_constraint);
+      ++current_constraint_diagnosis_depth;
+    }
 }
 
 diagnosing_failed_constraint::
 ~diagnosing_failed_constraint ()
 {
-  if (diagnosing_error && current_failed_constraint)
-    current_failed_constraint = TREE_CHAIN (current_failed_constraint);
+  if (diagnosing_error)
+    {
+      --current_constraint_diagnosis_depth;
+      if (current_failed_constraint)
+       current_failed_constraint = TREE_CHAIN (current_failed_constraint);
+    }
+
+}
+
+/* Whether we are allowed to replay an error that underlies a constraint 
failure
+   at the current diagnosis depth.  */
+
+bool
+diagnosing_failed_constraint::replay_errors_p ()
+{
+  if (current_constraint_diagnosis_depth >= concepts_diagnostics_max_depth)
+    {
+      concepts_diagnostics_max_depth_exceeded_p = true;
+      return false;
+    }
+  else
+    return true;
 }
 
 /* Emit diagnostics detailing the failure ARGS to satisfy the constraints
@@ -3403,11 +3498,26 @@ diagnose_constraints (location_t loc, tree t, tree args)
 {
   inform (loc, "constraints not satisfied");
 
+  if (concepts_diagnostics_max_depth == 0)
+    return;
+
   /* Replay satisfaction, but diagnose errors.  */
   if (!args)
     constraint_satisfaction_value (t, tf_warning_or_error);
   else
     constraint_satisfaction_value (t, args, tf_warning_or_error);
+
+  static bool suggested_p;
+  if (concepts_diagnostics_max_depth_exceeded_p
+      && current_constraint_diagnosis_depth == 0
+      && !suggested_p)
+    {
+      inform (UNKNOWN_LOCATION,
+             "set %qs to at least %d for more detail",
+             "-fconcepts-diagnostics-depth=",
+             concepts_diagnostics_max_depth + 1);
+      suggested_p = true;
+    }
 }
 
 #include "gt-cp-constraint.h"
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4e1d0f1d42e..af9c4516996 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7833,6 +7833,7 @@ struct diagnosing_failed_constraint
 {
   diagnosing_failed_constraint (tree, tree, bool);
   ~diagnosing_failed_constraint ();
+  static bool replay_errors_p ();
 
   bool diagnosing_error;
 };
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C 
b/gcc/testsuite/g++.dg/concepts/diagnostic2.C
index ce51b71fa8b..47accb8366e 100644
--- a/gcc/testsuite/g++.dg/concepts/diagnostic2.C
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C
@@ -5,7 +5,7 @@ template<typename T>
   inline constexpr bool foo_v = false;
 
 template<typename T>
-  concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" }
+  concept foo = foo_v<T> || foo_v<T&>; // { dg-message "no operand" }
 /* { dg-begin-multiline-output "" }
    concept foo = foo_v<T> || foo_v<T&>;
                  ~~~~~~~~~^~~~~~~~~~~~
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic5.C 
b/gcc/testsuite/g++.dg/concepts/diagnostic5.C
new file mode 100644
index 00000000000..2641dc18423
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic5.C
@@ -0,0 +1,46 @@
+// { dg-do compile { target c++2a } }
+// { dg-additional-options "-fconcepts-diagnostics-depth=2" }
+
+template<typename T>
+  concept c1 = requires { typename T::blah; };
+// { dg-message "satisfaction of .c1<T>. .with T = char." "" { target *-*-* } 
.-1 }
+// { dg-message "satisfaction of .c1<char\\*>." "" { target *-*-* } .-2 }
+// { dg-message ".typename T::blah. is invalid" "" { target *-*-* } .-3 }
+
+template<typename T>
+  concept c2 = requires (T x) { *x; };
+// { dg-message "satisfaction of .c2<T>. .with T = char." "" { target *-*-* } 
.-1 }
+// { dg-message "in requirements with .char x." "" { target *-*-* } .-2 }
+// { dg-message "required expression .* is invalid" "" { target *-*-* } .-3 }
+
+template<typename T>
+  concept c3 = __is_same(T, const T) || __is_same(T, int);
+// { dg-message "satisfaction of .c3<T>. .with T = char." "" { target *-*-* } 
.-1 }
+// { dg-message "no operand of the disjunction is satisfied" "" { target *-*-* 
} .-2 }
+
+template<typename T>
+  concept c4 = requires (T x) { requires c2<const T> || c2<volatile T>; };
+// { dg-message "satisfaction of .c4<T>. .with T = char." "" { target *-*-* } 
.-1 }
+// { dg-message "nested requirement" "" { target *-*-* } .-2 }
+
+template<typename T>
+  concept c5 = requires (T x) { { &x } -> c1; };
+// { dg-message "satisfaction of .c5<T>. .with T = char." "" { target *-*-* } 
.-1 }
+// { dg-message "in requirements with .char x." "" { target *-*-* } .-2 }
+// { dg-message "does not satisfy return-type-requirement" "" { target *-*-* } 
.-3 }
+// { dg-error "deduced expression type does not satisfy" "" { target *-*-* } 
.-4 }
+
+template<typename T>
+  requires (c1<T> || c2<T>) || (c3<T> || c4<T>) || c5<T> // { dg-message "49: 
no operand" }
+  // { dg-message ".c1<T>. is unsatisfied because" "" { target *-*-* } .-1 }
+  // { dg-message ".c2<T>. is unsatisfied because" "" { target *-*-* } .-2 }
+  // { dg-message ".c3<T>. is unsatisfied because" "" { target *-*-* } .-3 }
+  // { dg-message ".c4<T>. is unsatisfied because" "" { target *-*-* } .-4 }
+  // { dg-message ".c5<T>. is unsatisfied because" "" { target *-*-* } .-5 }
+  void foo() { }
+
+void
+bar()
+{
+  foo<char>(); // { dg-error "use of" }
+}
-- 
2.26.0.rc1.11.g30e9940356

Reply via email to