https://gcc.gnu.org/g:339246fb9ef4cac84126138093303f309071e40e

commit r15-5995-g339246fb9ef4cac84126138093303f309071e40e
Author: David Malcolm <dmalc...@redhat.com>
Date:   Fri Dec 6 13:40:55 2024 -0500

    c++: use diagnostic nesting [PR116253]
    
    This patch uses the nested diagnostics capabilities added in the earlier
    patch in the C++ frontend.
    
    With this, and enabling the non-standard text formatting via:
      -fdiagnostics-set-output=text:experimental-nesting=yes
    and using:
      -std=c++20 -fconcepts-diagnostics-depth=2
    then the output for the example in SG15's P3358R0 ("SARIF for Structured
    Diagnostics") is:
    
    P3358R0.C: In function ‘int main()’:
    P3358R0.C:26:6: error: no matching function for call to ‘pet(lizard)’
       26 |   pet(lizard{});
          |   ~~~^~~~~~~~~~
      • note: candidate: ‘template<class auto:1>  requires  pettable<auto:1> 
void pet(auto:1)’
        P3358R0.C:21:6:
           21 | void pet(pettable auto t);
              |      ^~~
        • note: template argument deduction/substitution failed:
          • note: constraints not satisfied
            • P3358R0.C: In substitution of ‘template<class auto:1>  requires  
pettable<auto:1> void pet(auto:1) [with auto:1 = lizard]’:
            • required from here
              P3358R0.C:26:6:
                 26 |   pet(lizard{});
                    |   ~~~^~~~~~~~~~
            • required for the satisfaction of ‘pettable<auto:1>’ [with auto:1 
= lizard]
              P3358R0.C:19:9:
                 19 | concept pettable = has_member_pet<T> or 
has_default_pet<T>;
                    |         ^~~~~~~~
            • note: no operand of the disjunction is satisfied
              P3358R0.C:19:38:
                 19 | concept pettable = has_member_pet<T> or 
has_default_pet<T>;
                    |                    ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
              • note: the operand ‘has_member_pet<T>’ is unsatisfied because
                P3358R0.C:19:20:
                   19 | concept pettable = has_member_pet<T> or 
has_default_pet<T>;
                      |                    
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                • required for the satisfaction of ‘has_member_pet<T>’ [with T 
= lizard]
                  P3358R0.C:13:9:
                     13 | concept has_member_pet = requires(T t) { t.pet(); };
                        |         ^~~~~~~~~~~~~~
                • required for the satisfaction of ‘pettable<auto:1>’ [with 
auto:1 = lizard]
                  P3358R0.C:19:9:
                     19 | concept pettable = has_member_pet<T> or 
has_default_pet<T>;
                        |         ^~~~~~~~
                • in requirements with ‘T t’ [with T = lizard]
                  P3358R0.C:13:26:
                     13 | concept has_member_pet = requires(T t) { t.pet(); };
                        |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~
                • note: the required expression ‘t.pet()’ is invalid, because
                  P3358R0.C:13:47:
                     13 | concept has_member_pet = requires(T t) { t.pet(); };
                        |                                          ~~~~~^~
                  • error: ‘struct lizard’ has no member named ‘pet’
                    P3358R0.C:13:44:
                       13 | concept has_member_pet = requires(T t) { t.pet(); };
                          |                                          ~~^~~
              • note: the operand ‘has_default_pet<T>’ is unsatisfied because
                P3358R0.C:19:41:
                   19 | concept pettable = has_member_pet<T> or 
has_default_pet<T>;
                      |                    
~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
                • required for the satisfaction of ‘has_default_pet<T>’ [with T 
= lizard]
                  P3358R0.C:16:9:
                     16 | concept has_default_pet = T::is_pettable;
                        |         ^~~~~~~~~~~~~~~
                • required for the satisfaction of ‘pettable<auto:1>’ [with 
auto:1 = lizard]
                  P3358R0.C:19:9:
                     19 | concept pettable = has_member_pet<T> or 
has_default_pet<T>;
                        |         ^~~~~~~~
                • error: ‘is_pettable’ is not a member of ‘lizard’
                  P3358R0.C:16:30:
                     16 | concept has_default_pet = T::is_pettable;
                        |                              ^~~~~~~~~~~
      • note: candidate: ‘void pet(dog)’
        P3358R0.C:9:6:
            9 | void pet(dog);
              |      ^~~
        • note: no known conversion for argument 1 from ‘lizard’ to ‘dog’
          P3358R0.C:9:10:
              9 | void pet(dog);
                |          ^~~
      • note: candidate: ‘void pet(cat)’
        P3358R0.C:10:6:
           10 | void pet(cat);
              |      ^~~
        • note: no known conversion for argument 1 from ‘lizard’ to ‘cat’
          P3358R0.C:10:10:
             10 | void pet(cat);
                |          ^~~
    
    showing the hierarchical structure of the messages; ideally there
    would be a UI here allowing the user to expand/collapse the messages
    to drill out into the detail they are interested in.
    
    The structure is also captured in SARIF output (via the "nestingLevel"
    property).
    
    gcc/cp/ChangeLog:
            PR other/116253
            * call.cc (print_conversion_rejection): Remove leading space from
            diagnostic messages.
            (print_conversion_rejection): Likewise.
            (print_arity_information): Likewise.
            (print_z_candidate): Likewise.  Add auto_diagnostic_nesting_level
            before calls to fn_type_unification and diagnose_constraints.
            (print_z_candidates): Add auto_diagnostic_nesting_level before
            looping over candidates.
            (conversion_null_warnings): Remove leading space from
            diagnostic messages.
            (maybe_inform_about_fndecl_for_bogus_argument_init): Likewise.
            * constraint.cc (tsubst_valid_expression_requirement): Add
            auto_diagnostic_nesting_level when showing why the expression is
            invalid.
            (satisfy_disjunction): Likewise when showing operans, and again
            when replaying each branch of the disjunction.
            (diagnose_constraints): Likewise when replaying satisfaction.
            * error.cc (cp_diagnostic_text_starter): Set prefix.
            (print_instantiation_full_context): Only show the file
            if we're not showing nesting or the user has opted in to
            showing location information in nested diagnostics.
            (class auto_context_line): New.
            (print_instantiation_partial_context_line): Replace calls to
            print_location and to diagnostic_show_locus with an
            auto_context_line.
            (print_instantiation_partial_context): Replace calls to
            print_location with an auto_context_line.
            (maybe_print_constexpr_context): Likewise.
            (print_constrained_decl_info): Likewise.
            (print_concept_check_info): Likewise.
            (print_constraint_context_head): Likewise.
            (print_requires_expression_info): Likewise.
    
    gcc/testsuite/ChangeLog:
            PR other/116253
            * g++.dg/concepts/nested-diagnostics-1-truncated.C: New test.
            * g++.dg/concepts/nested-diagnostics-1.C: New test.
            * g++.dg/concepts/nested-diagnostics-2.C: New test.
    
    Signed-off-by: David Malcolm <dmalc...@redhat.com>

Diff:
---
 gcc/cp/call.cc                                     | 69 ++++++++-------
 gcc/cp/constraint.cc                               |  5 ++
 gcc/cp/error.cc                                    | 99 ++++++++++++++++++----
 .../concepts/nested-diagnostics-1-truncated.C      | 41 +++++++++
 .../g++.dg/concepts/nested-diagnostics-1.C         | 51 +++++++++++
 .../g++.dg/concepts/nested-diagnostics-2.C         | 37 ++++++++
 6 files changed, 257 insertions(+), 45 deletions(-)

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 9fbc1602b0de..c4dc89f3205e 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -3886,18 +3886,18 @@ print_conversion_rejection (location_t loc, struct 
conversion_info *info,
       /* Conversion of implicit `this' argument failed.  */
       if (!TYPE_P (info->from))
        /* A bad conversion for 'this' must be discarding cv-quals.  */
-       inform (loc, "  passing %qT as %<this%> "
+       inform (loc, "passing %qT as %<this%> "
                "argument discards qualifiers",
                from);
       else
-       inform (loc, "  no known conversion for implicit "
+       inform (loc, "no known conversion for implicit "
                "%<this%> parameter from %qH to %qI",
                from, info->to_type);
     }
   else if (!TYPE_P (info->from))
     {
       if (info->n_arg >= 0)
-       inform (loc, "  conversion of argument %d would be ill-formed:",
+       inform (loc, "conversion of argument %d would be ill-formed:",
                info->n_arg + 1);
       iloc_sentinel ils = loc;
       perform_implicit_conversion (info->to_type, info->from,
@@ -3905,13 +3905,13 @@ print_conversion_rejection (location_t loc, struct 
conversion_info *info,
     }
   else if (info->n_arg == -2)
     /* Conversion of conversion function return value failed.  */
-    inform (loc, "  no known conversion from %qH to %qI",
+    inform (loc, "no known conversion from %qH to %qI",
            from, info->to_type);
   else
     {
       if (TREE_CODE (fn) == FUNCTION_DECL)
        loc = get_fndecl_argument_location (fn, info->n_arg);
-      inform (loc, "  no known conversion for argument %d from %qH to %qI",
+      inform (loc, "no known conversion for argument %d from %qH to %qI",
              info->n_arg + 1, from, info->to_type);
     }
 }
@@ -3925,13 +3925,13 @@ print_arity_information (location_t loc, unsigned int 
have, unsigned int want,
 {
   if (least_p)
     inform_n (loc, want,
-             "  candidate expects at least %d argument, %d provided",
-             "  candidate expects at least %d arguments, %d provided",
+             "candidate expects at least %d argument, %d provided",
+             "candidate expects at least %d arguments, %d provided",
              want, have);
   else
     inform_n (loc, want,
-             "  candidate expects %d argument, %d provided",
-             "  candidate expects %d arguments, %d provided",
+             "candidate expects %d argument, %d provided",
+             "candidate expects %d arguments, %d provided",
              want, have);
 }
 
@@ -3987,11 +3987,12 @@ print_z_candidate (location_t loc, const char *msgstr,
   if (fn != candidate->fn)
     {
       cloc = location_of (candidate->fn);
-      inform (cloc, "  inherited here");
+      inform (cloc, "inherited here");
     }
   /* Give the user some information about why this candidate failed.  */
   if (candidate->reason != NULL)
     {
+      auto_diagnostic_nesting_level sentinel;
       struct rejection_reason *r = candidate->reason;
 
       switch (r->code)
@@ -4008,13 +4009,13 @@ print_z_candidate (location_t loc, const char *msgstr,
          print_conversion_rejection (cloc, &r->u.bad_conversion, fn);
          break;
        case rr_explicit_conversion:
-         inform (cloc, "  return type %qT of explicit conversion function "
+         inform (cloc, "return type %qT of explicit conversion function "
                  "cannot be converted to %qT with a qualification "
                  "conversion", r->u.conversion.from,
                  r->u.conversion.to_type);
          break;
        case rr_template_conversion:
-         inform (cloc, "  conversion from return type %qT of template "
+         inform (cloc, "conversion from return type %qT of template "
                  "conversion function specialization to %qT is not an "
                  "exact match", r->u.conversion.from,
                  r->u.conversion.to_type);
@@ -4025,33 +4026,39 @@ print_z_candidate (location_t loc, const char *msgstr,
             them here.  */
          if (r->u.template_unification.tmpl == NULL_TREE)
            {
-             inform (cloc, "  substitution of deduced template arguments "
+             inform (cloc, "substitution of deduced template arguments "
                      "resulted in errors seen above");
              break;
            }
          /* Re-run template unification with diagnostics.  */
-         inform (cloc, "  template argument deduction/substitution failed:");
-         fn_type_unification (r->u.template_unification.tmpl,
-                              r->u.template_unification.explicit_targs,
-                              (make_tree_vec
-                               (r->u.template_unification.num_targs)),
-                              r->u.template_unification.args,
-                              r->u.template_unification.nargs,
-                              r->u.template_unification.return_type,
-                              r->u.template_unification.strict,
-                              r->u.template_unification.flags,
-                              NULL, true, false);
+         inform (cloc, "template argument deduction/substitution failed:");
+         {
+           auto_diagnostic_nesting_level sentinel;
+           fn_type_unification (r->u.template_unification.tmpl,
+                                r->u.template_unification.explicit_targs,
+                                (make_tree_vec
+                                 (r->u.template_unification.num_targs)),
+                                r->u.template_unification.args,
+                                r->u.template_unification.nargs,
+                                r->u.template_unification.return_type,
+                                r->u.template_unification.strict,
+                                r->u.template_unification.flags,
+                                NULL, true, false);
+         }
          break;
        case rr_invalid_copy:
          inform (cloc,
-                 "  a constructor taking a single argument of its own "
+                 "a constructor taking a single argument of its own "
                  "class type is invalid");
          break;
        case rr_constraint_failure:
-         diagnose_constraints (cloc, fn, NULL_TREE);
+         {
+           auto_diagnostic_nesting_level sentinel;
+           diagnose_constraints (cloc, fn, NULL_TREE);
+         }
          break;
        case rr_inherited_ctor:
-         inform (cloc, "  an inherited constructor is not a candidate for "
+         inform (cloc, "an inherited constructor is not a candidate for "
                  "initialization from an expression of the same or derived "
                  "type");
          break;
@@ -4124,6 +4131,8 @@ print_z_candidates (location_t loc, struct z_candidate 
*candidates,
   if (only_viable_p.is_unknown ())
     only_viable_p = candidates->viable == 1;
 
+  auto_diagnostic_nesting_level sentinel;
+
   for (; candidates; candidates = candidates->next)
     {
       if (only_viable_p.is_true () && candidates->viable != 1)
@@ -8336,7 +8345,7 @@ conversion_null_warnings (tree totype, tree expr, tree 
fn, int argnum)
                          "passing NULL to non-pointer argument %P of %qD",
                          argnum, fn))
            inform (get_fndecl_argument_location (fn, argnum),
-                   "  declared here");
+                   "declared here");
        }
       else
        warning_at (loc, OPT_Wconversion_null,
@@ -8355,7 +8364,7 @@ conversion_null_warnings (tree totype, tree expr, tree 
fn, int argnum)
                          "converting %<false%> to pointer type for argument "
                          "%P of %qD", argnum, fn))
            inform (get_fndecl_argument_location (fn, argnum),
-                   "  declared here");
+                   "declared here");
        }
       else
        warning_at (loc, OPT_Wconversion_null,
@@ -8430,7 +8439,7 @@ maybe_inform_about_fndecl_for_bogus_argument_init (tree 
fn, int argnum,
       gcc_rich_location richloc (get_fndecl_argument_location (fn, argnum));
       richloc.set_highlight_color (highlight_color);
       inform (&richloc,
-             "  initializing argument %P of %qD", argnum, fn);
+             "initializing argument %P of %qD", argnum, fn);
     }
 }
 
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 3389775036c1..2af1bbc016b9 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1324,6 +1324,7 @@ tsubst_valid_expression_requirement (tree t, tree args, 
sat_info info)
       if (diagnosing_failed_constraint::replay_errors_p ())
        {
          inform (loc, "the required expression %qE is invalid, because", t);
+         auto_diagnostic_nesting_level sentinel;
          if (r == error_mark_node)
            tsubst_expr (t, args, info.complain, info.in_decl);
          else
@@ -2232,6 +2233,7 @@ satisfy_disjunction (tree t, tree args, sat_info info)
              "no operand of the disjunction is satisfied");
       if (diagnosing_failed_constraint::replay_errors_p ())
        {
+         auto_diagnostic_nesting_level sentinel;
          /* Replay the error in each branch of the disjunction.  */
          auto_vec<tree_pair> operands;
          collect_operands_of_disjunction (t, &operands);
@@ -2243,6 +2245,7 @@ satisfy_disjunction (tree t, tree args, sat_info info)
                                              disj_expr.get_start (),
                                              disj_expr.get_finish ());
              inform (loc, "the operand %qE is unsatisfied because", op);
+             auto_diagnostic_nesting_level sentinel;
              satisfy_constraint_r (norm_op, args, info);
            }
        }
@@ -3293,6 +3296,8 @@ diagnose_constraints (location_t loc, tree t, tree args)
   if (concepts_diagnostics_max_depth == 0)
     return;
 
+  auto_diagnostic_nesting_level sentinel;
+
   /* Replay satisfaction, but diagnose unsatisfaction.  */
   sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
   constraint_satisfaction_value (t, args, noisy);
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index b3ab8fa8a7f7..3b22a8f40723 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -3647,6 +3647,8 @@ static void
 cp_diagnostic_text_starter (diagnostic_text_output_format &text_output,
                            const diagnostic_info *diagnostic)
 {
+  pp_set_prefix (text_output.get_printer (),
+                text_output.build_indent_prefix (true));
   text_output.report_current_module (diagnostic_location (diagnostic));
   cp_print_error_function (text_output, diagnostic);
   maybe_print_instantiation_context (text_output);
@@ -3814,13 +3816,19 @@ print_instantiation_full_context 
(diagnostic_text_output_format &text_output)
 
   if (p)
     {
+      bool show_file
+       = ((!text_output.show_nesting_p ())
+          || text_output.show_locations_in_nesting_p ());
+      char *indent = text_output.build_indent_prefix (true);
       pp_verbatim (text_output.get_printer (),
                   p->list_p ()
-                  ? _("%s: In substitution of %qS:\n")
-                  : _("%s: In instantiation of %q#D:\n"),
-                  LOCATION_FILE (location),
+                  ? _("%s%s%sIn substitution of %qS:\n")
+                  : _("%s%s%sIn instantiation of %q#D:\n"),
+                  indent,
+                  show_file ? LOCATION_FILE (location) : "",
+                  show_file ? ": " : "",
                   p->get_node ());
-
+      free (indent);
       location = p->locus;
       p = p->next;
     }
@@ -3842,6 +3850,71 @@ print_location (diagnostic_text_output_format 
&text_output,
                 "locus", xloc.file, xloc.line);
 }
 
+/* A RAII class for use when emitting a line of contextual information
+   via pp_verbatim to a diagnostic_text_output_format to add before/after
+   behaviors to the pp_verbatim calls.
+
+   If the text output has show_nesting_p (), then the ctor prints
+   leading indentation and a bullet point, and the dtor prints
+   the location on a new line, and calls diagnostic_show_locus, both
+   with indentation (and no bullet point).
+
+   Otherwise (when the text output has !show_nesting_p), the ctor prints
+   the location as leading information on the same line, and the
+   dtor optionally calls diagnostic_show_locus.  */
+
+class auto_context_line
+{
+public:
+  auto_context_line (diagnostic_text_output_format &text_output,
+                    location_t loc,
+                    bool show_locus = false)
+  : m_text_output (text_output),
+    m_loc (loc),
+    m_show_locus (show_locus)
+  {
+    char *indent = m_text_output.build_indent_prefix (true);
+    pp_verbatim (m_text_output.get_printer (), indent);
+    free (indent);
+    if (!m_text_output.show_nesting_p ())
+      print_location (m_text_output, m_loc);
+  }
+  ~auto_context_line ()
+  {
+    pretty_printer *const pp = m_text_output.get_printer ();
+    if (m_text_output.show_nesting_p ())
+      {
+       if (m_text_output.show_locations_in_nesting_p ())
+         {
+           char *indent = m_text_output.build_indent_prefix (false);
+           pp_verbatim (pp, indent);
+           print_location (m_text_output, m_loc);
+           pp_newline (pp);
+
+           char *saved_prefix = pp_take_prefix (pp);
+           pp_set_prefix (pp, indent);
+           gcc_rich_location rich_loc (m_loc);
+           diagnostic_show_locus (&m_text_output.get_context (), &rich_loc,
+                                  DK_NOTE, pp);
+           pp_set_prefix (pp, saved_prefix);
+         }
+      }
+    else if (m_show_locus)
+      {
+       char *saved_prefix = pp_take_prefix (pp);
+       pp_set_prefix (pp, nullptr);
+       gcc_rich_location rich_loc (m_loc);
+       diagnostic_show_locus (&m_text_output.get_context (), &rich_loc,
+                              DK_NOTE, pp);
+       pp_set_prefix (pp, saved_prefix);
+      }
+  }
+private:
+  diagnostic_text_output_format &m_text_output;
+  location_t m_loc;
+  bool m_show_locus;
+};
+
 /* Helper function of print_instantiation_partial_context() that
    prints a single line of instantiation context.  */
 
@@ -3853,7 +3926,7 @@ print_instantiation_partial_context_line 
(diagnostic_text_output_format &text_ou
   if (loc == UNKNOWN_LOCATION)
     return;
 
-  print_location (text_output, loc);
+  auto_context_line sentinel (text_output, loc, true);
 
   pretty_printer *const pp = text_output.get_printer ();
 
@@ -3879,10 +3952,6 @@ print_instantiation_partial_context_line 
(diagnostic_text_output_format &text_ou
                   ? _("recursively required from here\n")
                   : _("required from here\n"));
     }
-  gcc_rich_location rich_loc (loc);
-  char *saved_prefix = pp_take_prefix (pp);
-  diagnostic_show_locus (&text_output.get_context (), &rich_loc, DK_NOTE, pp);
-  pp_set_prefix (pp, saved_prefix);
 }
 
 /* Same as print_instantiation_full_context but less verbose.  */
@@ -3929,7 +3998,7 @@ print_instantiation_partial_context 
(diagnostic_text_output_format &text_output,
        }
       if (t != NULL && skip > 0)
        {
-         print_location (text_output, loc);
+         auto_context_line sentinel (text_output, loc);
          pp_verbatim (text_output.get_printer (),
                       _("[ skipping %d instantiation contexts,"
                         " use -ftemplate-backtrace-limit=0 to disable ]\n"),
@@ -3981,7 +4050,7 @@ maybe_print_constexpr_context 
(diagnostic_text_output_format &text_output)
     {
       const char *s = expr_as_string (t, 0);
       pretty_printer *const pp = text_output.get_printer ();
-      print_location (text_output, EXPR_LOCATION (t));
+      auto_context_line sentinel (text_output, EXPR_LOCATION (t));
       pp_verbatim (pp,
                   _("in %<constexpr%> expansion of %qs"),
                   s);
@@ -3994,7 +4063,7 @@ static void
 print_constrained_decl_info (diagnostic_text_output_format &text_output,
                             tree decl)
 {
-  print_location (text_output, DECL_SOURCE_LOCATION (decl));
+  auto_context_line sentinel (text_output, DECL_SOURCE_LOCATION (decl));
   pretty_printer *const pp = text_output.get_printer ();
   pp_verbatim (pp, "required by the constraints of %q#D\n", decl);
 }
@@ -4007,7 +4076,7 @@ print_concept_check_info (diagnostic_text_output_format 
&text_output,
 
   tree tmpl = TREE_OPERAND (expr, 0);
 
-  print_location (text_output, DECL_SOURCE_LOCATION (tmpl));
+  auto_context_line sentinel (text_output, DECL_SOURCE_LOCATION (tmpl));
 
   cxx_pretty_printer *const pp
     = (cxx_pretty_printer *)text_output.get_printer ();
@@ -4031,7 +4100,7 @@ print_constraint_context_head 
(diagnostic_text_output_format &text_output,
   tree src = TREE_VALUE (cxt);
   if (!src)
     {
-      print_location (text_output, input_location);
+      auto_context_line sentinel (text_output, input_location);
       pretty_printer *const pp = text_output.get_printer ();
       pp_verbatim (pp, "required for constraint satisfaction\n");
       return NULL_TREE;
@@ -4059,7 +4128,7 @@ print_requires_expression_info 
(diagnostic_text_output_format &text_output,
   if (map == error_mark_node)
     return;
 
-  print_location (text_output, cp_expr_loc_or_input_loc (expr));
+  auto_context_line sentinel (text_output, cp_expr_loc_or_input_loc (expr));
   cxx_pretty_printer *const pp
     = static_cast <cxx_pretty_printer *> (text_output.get_printer ());
   pp_verbatim (pp, "in requirements ");
diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C 
b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C
new file mode 100644
index 000000000000..5965e25f2563
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-fconcepts" }
+// { dg-additional-options 
"-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no"
 }
+
+struct dog {};
+struct cat {};
+
+void pet(dog);
+void pet(cat);
+
+template <class T>
+concept has_member_pet = requires(T t) { t.pet(); };
+
+template <class T>
+concept has_default_pet = T::is_pettable;
+
+template <class T>
+concept pettable = has_member_pet<T> or has_default_pet<T>;
+
+void pet(pettable auto t);
+
+struct lizard {};
+
+int main() {
+  pet(lizard{}); // { dg-error "no matching function for call to 
'pet\\\(lizard\\\)'" }
+}
+
+/* { dg-begin-multiline-output "" }
+  * note: candidate: 'template<class auto:1>  requires  pettable<auto:1> void 
pet(auto:1)'
+    * note: template argument deduction/substitution failed:
+      * note: constraints not satisfied
+        * In substitution of 'template<class auto:1>  requires  
pettable<auto:1> void pet(auto:1) [with auto:1 = lizard]':
+        * required from here
+        * required for the satisfaction of 'pettable<auto:1>' [with auto:1 = 
lizard]
+        * note: no operand of the disjunction is satisfied
+        * note: set '-fconcepts-diagnostics-depth=' to at least 2 for more 
detail
+  * note: candidate: 'void pet(dog)'
+    * note: no known conversion for argument 1 from 'lizard' to 'dog'
+  * note: candidate: 'void pet(cat)'
+    * note: no known conversion for argument 1 from 'lizard' to 'cat'
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C 
b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C
new file mode 100644
index 000000000000..a4a2f4eb139f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-fconcepts" }
+// { dg-additional-options 
"-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no"
 }
+// { dg-additional-options "-fconcepts-diagnostics-depth=3" }
+
+struct dog {};
+struct cat {};
+
+void pet(dog);
+void pet(cat);
+
+template <class T>
+concept has_member_pet = requires(T t) { t.pet(); };
+
+template <class T>
+concept has_default_pet = T::is_pettable;
+
+template <class T>
+concept pettable = has_member_pet<T> or has_default_pet<T>;
+
+void pet(pettable auto t);
+
+struct lizard {};
+
+int main() {
+  pet(lizard{}); // { dg-error "no matching function for call to 
'pet\\\(lizard\\\)'" }
+}
+
+/* { dg-begin-multiline-output "" }
+  * note: candidate: 'template<class auto:1>  requires  pettable<auto:1> void 
pet(auto:1)'
+    * note: template argument deduction/substitution failed:
+      * note: constraints not satisfied
+        * In substitution of 'template<class auto:1>  requires  
pettable<auto:1> void pet(auto:1) [with auto:1 = lizard]':
+        * required from here
+        * required for the satisfaction of 'pettable<auto:1>' [with auto:1 = 
lizard]
+        * note: no operand of the disjunction is satisfied
+          * note: the operand 'has_member_pet<T>' is unsatisfied because
+            * required for the satisfaction of 'has_member_pet<T>' [with T = 
lizard]
+            * required for the satisfaction of 'pettable<auto:1>' [with auto:1 
= lizard]
+            * in requirements with 'T t' [with T = lizard]
+            * note: the required expression 't.pet()' is invalid, because
+              * error: 'struct lizard' has no member named 'pet'
+          * note: the operand 'has_default_pet<T>' is unsatisfied because
+            * required for the satisfaction of 'has_default_pet<T>' [with T = 
lizard]
+            * required for the satisfaction of 'pettable<auto:1>' [with auto:1 
= lizard]
+            * error: 'is_pettable' is not a member of 'lizard'
+  * note: candidate: 'void pet(dog)'
+    * note: no known conversion for argument 1 from 'lizard' to 'dog'
+  * note: candidate: 'void pet(cat)'
+    * note: no known conversion for argument 1 from 'lizard' to 'cat'
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C 
b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C
new file mode 100644
index 000000000000..0c5cc0152979
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++17 } }
+// { dg-options "-fconcepts" }
+// { dg-additional-options 
"-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no"
 }
+
+struct dog{};
+struct cat{};
+
+void pet(dog);
+void pet(cat);
+
+template <class T>
+concept pettable = requires(T t) { t.pet(); };
+
+template <pettable T>
+void pet(T);
+
+struct donkey {};
+
+int main() {
+  pet(donkey{}); // { dg-error "no matching function for call to 
'pet\\\(donkey\\\)'" }
+}
+
+/* { dg-begin-multiline-output "" }
+  * note: candidate: 'template<class T>  requires  pettable<T> void pet(T)'
+    * note: template argument deduction/substitution failed:
+      * note: constraints not satisfied
+        * In substitution of 'template<class T>  requires  pettable<T> void 
pet(T) [with T = donkey]':
+        * required from here
+        * required for the satisfaction of 'pettable<T>' [with T = donkey]
+        * in requirements with 'T t' [with T = donkey]
+        * note: the required expression 't.pet()' is invalid
+        * note: set '-fconcepts-diagnostics-depth=' to at least 2 for more 
detail
+  * note: candidate: 'void pet(dog)'
+    * note: no known conversion for argument 1 from 'donkey' to 'dog'
+  * note: candidate: 'void pet(cat)'
+    * note: no known conversion for argument 1 from 'donkey' to 'cat'
+   { dg-end-multiline-output "" } */

Reply via email to