public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] PR c++/92439: Improve diagnostics for ill-formed requires-clauses
@ 2019-11-22 16:08 Andrew Sutton
  2019-11-26 21:41 ` Jason Merrill
  0 siblings, 1 reply; 2+ messages in thread
From: Andrew Sutton @ 2019-11-22 16:08 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 114 bytes --]

This does a much better job identifying when an expression in a
requires-clause needs parentheses.

Andrew Sutton

[-- Attachment #2: 0001-PR-c-92439.patch --]
[-- Type: application/octet-stream, Size: 21327 bytes --]

From 816f5fe7d52483a1b8c108021ed72aa90de6fde4 Mon Sep 17 00:00:00 2001
From: Andrew Sutton <asutton@lock3software.com>
Date: Fri, 22 Nov 2019 10:25:11 -0500
Subject: [PATCH 1/1] PR c++/92439.

Improve quality of diagnostics for subexpressions that need parens.

gcc/cp/
	* parser.c (cp_parser_requires_clause_opt): Add a flag to indicate
	when parsing a requires-clause before lambda parameters, and...
	(cp_parser_lambda_declarator_opt): ... use that here ...
	(cp_parser_type_parameter): ... and here ...
	(cp_parser_late_return_type_opt): ... and here ...
	(cp_parser_explicit_template_declaration): ... and here.
	(cp_parser_diagnose_ungrouped_constraint_plain): Adjust the message
	because this can apply to subexpressions that are not immediately
	after a requires-clause.
	(cp_parser_diagnose_ungrouped_constraint_rich): Likewise.
	(primary_constraint_error): New.
	(cp_parser_constraint_requires_parens): New.
	(cp_parser_unary_constraint_requires_parens): New.
	(cp_parser_constraint_primary_expression): Check for unary expressions
	before parsing the primary expression. Also check for binary and
	postfix operators after a successful parse of the primary expression.
	Force a re-parse if the result would form a lower-precedence string.
	(cp_parser_constraint_logical_and_expression): Propagate lambda flag;
	move checks for ill-formed constraints into the constraint primary
	expression.
	(cp_parser_constraint_logical_or_expression): Likewise.
	(cp_parser_requires_clause_expression): Propagate lambda flag.

gcc/testsuite/
	* g++.dg/cpp2a/concepts-requires20.C: New.
---
 gcc/cp/ChangeLog                              |  27 ++
 gcc/cp/parser.c                               | 276 +++++++++++-------
 gcc/testsuite/ChangeLog                       |   5 +
 .../g++.dg/cpp2a/concepts-requires20.C        |  65 +++++
 4 files changed, 274 insertions(+), 99 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 507741bf2ff..d3debce09f1 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,30 @@
+2019-11-22  Andrew Sutton  <asutton@lock3software.com>
+
+	PR c++/92439
+	Improve quality of diagnostics for subexpressions that need parens.
+	* parser.c (cp_parser_requires_clause_opt): Add a flag to indicate
+	when parsing a requires-clause before lambda parameters, and...
+	(cp_parser_lambda_declarator_opt): ... use that here ...
+	(cp_parser_type_parameter): ... and here ...
+	(cp_parser_late_return_type_opt): ... and here ...
+	(cp_parser_explicit_template_declaration): ... and here.
+	(cp_parser_diagnose_ungrouped_constraint_plain): Adjust the message
+	because this can apply to subexpressions that are not immediately
+	after a requires-clause.
+	(cp_parser_diagnose_ungrouped_constraint_rich): Likewise.
+	(primary_constraint_error): New.
+	(cp_parser_constraint_requires_parens): New.
+	(cp_parser_unary_constraint_requires_parens): New.
+	(cp_parser_constraint_primary_expression): Check for unary expressions
+	before parsing the primary expression. Also check for binary and
+	postfix operators after a successful parse of the primary expression.
+	Force a re-parse if the result would form a lower-precedence string.
+	(cp_parser_constraint_logical_and_expression): Propagate lambda flag;
+	move checks for ill-formed constraints into the constraint primary
+	expression.
+	(cp_parser_constraint_logical_or_expression): Likewise.
+	(cp_parser_requires_clause_expression): Propagate lambda flag.
+
 2019-11-21  Jakub Jelinek  <jakub@redhat.com>
 	    Jason Merrill  <jason@redhat.com>
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 03b1ec748c7..309d2384a97 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2440,7 +2440,7 @@ static tree cp_parser_concept_definition
 static tree cp_parser_constraint_expression
   (cp_parser *);
 static tree cp_parser_requires_clause_opt
-  (cp_parser *);
+  (cp_parser *, bool);
 static tree cp_parser_requires_expression
   (cp_parser *);
 static tree cp_parser_requirement_parameter_list
@@ -10897,7 +10897,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       /* We may have a constrained generic lambda; parse the requires-clause
 	 immediately after the template-parameter-list and combine with any
 	 shorthand constraints present.  */
-      tree dreqs = cp_parser_requires_clause_opt (parser);
+      tree dreqs = cp_parser_requires_clause_opt (parser, true);
       if (flag_concepts)
 	{
 	  tree reqs = get_shorthand_constraints (current_template_parms);
@@ -10990,7 +10990,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 	gnu_attrs = cp_parser_gnu_attributes_opt (parser);
 
       /* Parse optional trailing requires clause.  */
-      trailing_requires_clause = cp_parser_requires_clause_opt (parser);
+      trailing_requires_clause = cp_parser_requires_clause_opt (parser, false);
 
       /* The function parameters must be in scope all the way until after the
          trailing-return-type in case of decltype.  */
@@ -16328,11 +16328,11 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack)
 	/* Look for the `>'.  */
 	cp_parser_require (parser, CPP_GREATER, RT_GREATER);
 
-        // If template requirements are present, parse them.
+	/* If template requirements are present, parse them.  */
 	if (flag_concepts)
           {
 	    tree reqs = get_shorthand_constraints (current_template_parms);
-	    if (tree dreqs = cp_parser_requires_clause_opt (parser))
+	    if (tree dreqs = cp_parser_requires_clause_opt (parser, false))
               reqs = combine_constraint_expressions (reqs, dreqs);
 	    TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
           }
@@ -21940,7 +21940,7 @@ cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator,
 
   /* Function declarations may be followed by a trailing
      requires-clause.  */
-  requires_clause = cp_parser_requires_clause_opt (parser);
+  requires_clause = cp_parser_requires_clause_opt (parser, false);
 
   if (declare_simd_p)
     declarator->attributes
@@ -27112,8 +27112,7 @@ cp_parser_concept_definition (cp_parser *parser)
 static void
 cp_parser_diagnose_ungrouped_constraint_plain (location_t loc)
 {
-  error_at (loc, "expression after %<requires%> must be enclosed "
-		 "in parentheses");
+  error_at (loc, "expression must be enclosed in parentheses");
 }
 
 static void
@@ -27122,56 +27121,48 @@ cp_parser_diagnose_ungrouped_constraint_rich (location_t loc)
   gcc_rich_location richloc (loc);
   richloc.add_fixit_insert_before ("(");
   richloc.add_fixit_insert_after (")");
-  error_at (&richloc, "expression after %<requires%> must be enclosed "
-		      "in parentheses");
+  error_at (&richloc, "expression must be enclosed in parentheses");
 }
 
-/* Parse a primary expression within a constraint.  */
-
-static cp_expr
-cp_parser_constraint_primary_expression (cp_parser *parser)
+/* Characterizes the likely kind of expression intended by a mis-written
+   primary constraint.  */
+enum primary_constraint_error
 {
-  cp_parser_parse_tentatively (parser);
-  cp_id_kind idk;
-  location_t loc = input_location;
-  cp_expr expr = cp_parser_primary_expression (parser,
-					       /*address_p=*/false,
-					       /*cast_p=*/false,
-					       /*template_arg_p=*/false,
-					       &idk);
-  expr.maybe_add_location_wrapper ();
-  if (expr != error_mark_node)
-    expr = finish_constraint_primary_expr (expr);
-  if (cp_parser_parse_definitely (parser))
-    return expr;
-
-  /* Retry the parse at a lower precedence. If that succeeds, diagnose the
-     error, but return the expression as if it were valid.  */
-  cp_parser_parse_tentatively (parser);
-  expr = cp_parser_simple_cast_expression (parser);
-  if (cp_parser_parse_definitely (parser))
-    {
-      cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location());
-      return expr;
-    }
-
-  /* Otherwise, something has gone wrong, but we can't generate a more
-     meaningful diagnostic or recover.  */
-  cp_parser_diagnose_ungrouped_constraint_plain (loc);
-  return error_mark_node;
-}
+  pce_ok,
+  pce_maybe_operator,
+  pce_maybe_postfix,
+};
 
-/* Examine the token following EXPR. If it is an operator in a non-logical
-   binary expression, diagnose that as an error. Returns ERROR_MARK_NODE.  */
+/* Returns true if the token(s) following a primary-expression in a
+   constraint-logical-* expression would require parentheses.  */
 
-static cp_expr
-cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
+static primary_constraint_error
+cp_parser_constraint_requires_parens (cp_parser *parser, bool lambda_p)
 {
   cp_token *token = cp_lexer_peek_token (parser->lexer);
   switch (token->type)
     {
       default:
-        return lhs;
+	return pce_ok;
+
+      case CPP_EQ:
+	{
+	  /* An equal sign may be part of the the definition of a function,
+	     and not an assignment operator, when parsing the expression
+	     for a trailing requires-clause. For example:
+
+		template<typename T>
+		struct S {
+		  S() requires C<T> = default;
+		};
+
+	     Don't try to reparse this a binary operator.  */
+	  if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE)
+	      || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT))
+	    return pce_ok;
+
+	  gcc_fallthrough ();
+	}
 
       /* Arithmetic operators.  */
       case CPP_PLUS:
@@ -27186,15 +27177,13 @@ cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
       case CPP_RSHIFT:
       case CPP_LSHIFT:
       /* Relational operators.  */
-      /* FIXME: Handle '<=>'.  */
       case CPP_EQ_EQ:
       case CPP_NOT_EQ:
       case CPP_LESS:
       case CPP_GREATER:
       case CPP_LESS_EQ:
       case CPP_GREATER_EQ:
-      /* Conditional operator */
-      case CPP_QUERY:
+      case CPP_SPACESHIP:
       /* Pointer-to-member.  */
       case CPP_DOT_STAR:
       case CPP_DEREF_STAR:
@@ -27209,52 +27198,138 @@ cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
       case CPP_XOR_EQ:
       case CPP_RSHIFT_EQ:
       case CPP_LSHIFT_EQ:
-        break;
+      /* Conditional operator */
+      case CPP_QUERY:
+	/* Unenclosed binary or conditional operator.  */
+	return pce_maybe_operator;
 
-      case CPP_EQ: {
-	/* An equal sign may be part of the the definition of a function,
-	   and not an assignment operator, when parsing the expression
-	   for a trailing requires-clause. For example:
+      case CPP_OPEN_PAREN:
+	{
+	  /* A primary constraint that precedes the parameter-list of a
+	     lambda expression is followed by an open paren.
 
-	      template<typename T>
-	      struct S {
-		S() requires C<T> = default;
-	      }
+		[]<typename T> requires C (T a, T b) { ... }
 
-	   This is not an error.  */
-	if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE)
-	    || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT))
-	  return lhs;
+	     Don't try to re-parse this as a postfix expression.  */
+	  if (lambda_p)
+	    return pce_ok;
 
-        break;
-      }
+	  gcc_fallthrough ();
+	}
+      case CPP_OPEN_SQUARE:
+      case CPP_PLUS_PLUS:
+      case CPP_MINUS_MINUS:
+      case CPP_DOT:
+      case CPP_DEREF:
+	/* Unenclosed postfix operator.  */
+	return pce_maybe_postfix;
    }
+}
+
+/* Returns true if the next token begins a unary expression, preceded by
+   an operator or keyword.  */
+
+static bool
+cp_parser_unary_constraint_requires_parens (cp_parser *parser)
+{
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  switch (token->type)
+    {
+      case CPP_NOT:
+      case CPP_PLUS:
+      case CPP_MINUS:
+      case CPP_MULT:
+      case CPP_COMPL:
+      case CPP_PLUS_PLUS:
+      case CPP_MINUS_MINUS:
+	return true;
+
+      case CPP_KEYWORD:
+	{
+	  switch (token->keyword)
+	    {
+	      case RID_STATCAST:
+	      case RID_DYNCAST:
+	      case RID_REINTCAST:
+	      case RID_CONSTCAST:
+	      case RID_TYPEID:
+	      case RID_SIZEOF:
+	      case RID_ALIGNOF:
+	      case RID_NOEXCEPT:
+	      case RID_NEW:
+	      case RID_DELETE:
+	      case RID_THROW:
+		return true;
+
+	     default:
+		break;
+	  }
+	}
+
+      default:
+	break;
+    }
+
+  return false;
+}
+
+/* Parse a primary expression within a constraint.  */
+
+static cp_expr
+cp_parser_constraint_primary_expression (cp_parser *parser, bool lambda_p)
+{
+  /* If this looks like a unary expression, parse it as such, but diagnose
+     it as ill-formed; it requires parens.  */
+  if (cp_parser_unary_constraint_requires_parens (parser))
+    {
+      cp_expr e = cp_parser_assignment_expression (parser, NULL, false, false);
+      cp_parser_diagnose_ungrouped_constraint_rich (e.get_location());
+      return e;
+    }
 
-   /* Try to parse the RHS as either the remainder of a conditional-expression
-      or a logical-or-expression so we can form a good diagnostic.  */
   cp_parser_parse_tentatively (parser);
-  cp_expr rhs;
-  if (token->type == CPP_QUERY)
-    rhs = cp_parser_question_colon_clause (parser, lhs);
-  else
+  cp_id_kind idk;
+  location_t loc = input_location;
+  cp_expr expr = cp_parser_primary_expression (parser,
+					       /*address_p=*/false,
+					       /*cast_p=*/false,
+					       /*template_arg_p=*/false,
+					       &idk);
+  expr.maybe_add_location_wrapper ();
+
+  primary_constraint_error pce = pce_ok;
+  if (expr != error_mark_node)
     {
-      cp_lexer_consume_token (parser->lexer);
-      rhs = cp_parser_binary_expression (parser, false, false, false,
-					 PREC_NOT_OPERATOR, NULL);
+      /* The primary-expression could be part of an unenclosed non-logical
+	 compound expression.  */
+      pce = cp_parser_constraint_requires_parens (parser, lambda_p);
+      if (pce != pce_ok)
+	cp_parser_simulate_error (parser);
+      else
+	expr = finish_constraint_primary_expr (expr);
     }
+  if (cp_parser_parse_definitely (parser))
+    return expr;
+  if (expr == error_mark_node)
+    return error_mark_node;
 
-  /* If we couldn't parse the RHS, then emit the best diagnostic we can.  */
-  if (!cp_parser_parse_definitely (parser))
+  /* Retry the parse at a lower precedence. If that succeeds, diagnose the
+     error, but return the expression as if it were valid.  */
+  gcc_assert (pce != pce_ok);
+  cp_parser_parse_tentatively (parser);
+  if (pce == pce_maybe_operator)
+    expr = cp_parser_assignment_expression (parser, NULL, false, false);
+  else
+    expr = cp_parser_simple_cast_expression (parser);
+  if (cp_parser_parse_definitely (parser))
     {
-      cp_parser_diagnose_ungrouped_constraint_plain (token->location);
-      return error_mark_node;
+      cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location());
+      return expr;
     }
 
-  /* Otherwise, emit a fixit for the complete binary expression.  */
-  location_t loc = make_location (token->location,
-				  lhs.get_start(),
-				  rhs.get_finish());
-  cp_parser_diagnose_ungrouped_constraint_rich (loc);
+  /* Otherwise, something has gone very wrong, and we can't generate a more
+     meaningful diagnostic or recover.  */
+  cp_parser_diagnose_ungrouped_constraint_plain (loc);
   return error_mark_node;
 }
 
@@ -27265,16 +27340,16 @@ cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
        constraint-logical-and-expression '&&' primary-expression  */
 
 static cp_expr
-cp_parser_constraint_logical_and_expression (cp_parser *parser)
+cp_parser_constraint_logical_and_expression (cp_parser *parser, bool lambda_p)
 {
-  cp_expr lhs = cp_parser_constraint_primary_expression (parser);
+  cp_expr lhs = cp_parser_constraint_primary_expression (parser, lambda_p);
   while (cp_lexer_next_token_is (parser->lexer, CPP_AND_AND))
     {
       cp_token *op = cp_lexer_consume_token (parser->lexer);
-      tree rhs = cp_parser_constraint_primary_expression (parser);
+      tree rhs = cp_parser_constraint_primary_expression (parser, lambda_p);
       lhs = finish_constraint_and_expr (op->location, lhs, rhs);
     }
-  return cp_parser_check_non_logical_constraint (parser, lhs);
+  return lhs;
 }
 
 /* Parse a constraint-logical-or-expression.
@@ -27284,27 +27359,27 @@ cp_parser_constraint_logical_and_expression (cp_parser *parser)
        constraint-logical-or-expression '||' constraint-logical-and-expression  */
 
 static cp_expr
-cp_parser_constraint_logical_or_expression (cp_parser *parser)
+cp_parser_constraint_logical_or_expression (cp_parser *parser, bool lambda_p)
 {
-  cp_expr lhs = cp_parser_constraint_logical_and_expression (parser);
+  cp_expr lhs = cp_parser_constraint_logical_and_expression (parser, lambda_p);
   while (cp_lexer_next_token_is (parser->lexer, CPP_OR_OR))
     {
       cp_token *op = cp_lexer_consume_token (parser->lexer);
-      cp_expr rhs = cp_parser_constraint_logical_and_expression (parser);
+      cp_expr rhs = cp_parser_constraint_logical_and_expression (parser, lambda_p);
       lhs = finish_constraint_or_expr (op->location, lhs, rhs);
     }
-  return cp_parser_check_non_logical_constraint (parser, lhs);
+  return lhs;
 }
 
 /* Parse the expression after a requires-clause. This has a different grammar
     than that in the concepts TS.  */
 
 static tree
-cp_parser_requires_clause_expression (cp_parser *parser)
+cp_parser_requires_clause_expression (cp_parser *parser, bool lambda_p)
 {
   processing_constraint_expression_sentinel parsing_constraint;
   ++processing_template_decl;
-  cp_expr expr = cp_parser_constraint_logical_or_expression (parser);
+  cp_expr expr = cp_parser_constraint_logical_or_expression (parser, lambda_p);
   if (check_for_bare_parameter_packs (expr))
     expr = error_mark_node;
   --processing_template_decl;
@@ -27337,12 +27412,15 @@ cp_parser_constraint_expression (cp_parser *parser)
 /* Optionally parse a requires clause:
 
       requires-clause:
-        `requires` constraint-logical-or-expression.
+	`requires` constraint-logical-or-expression.
    [ConceptsTS]
-        `requires constraint-expression.  */
+	`requires constraint-expression.
+
+   LAMBDA_P is true when the requires-clause is parsed before the
+   parameter-list of a lambda-declarator.  */
 
 static tree
-cp_parser_requires_clause_opt (cp_parser *parser)
+cp_parser_requires_clause_opt (cp_parser *parser, bool lambda_p)
 {
   cp_token *tok = cp_lexer_peek_token (parser->lexer);
   if (tok->keyword != RID_REQUIRES)
@@ -27362,7 +27440,7 @@ cp_parser_requires_clause_opt (cp_parser *parser)
   cp_lexer_consume_token (parser->lexer);
 
   if (!flag_concepts_ts)
-    return cp_parser_requires_clause_expression (parser);
+    return cp_parser_requires_clause_expression (parser, lambda_p);
   else
     return cp_parser_constraint_expression (parser);
 }
@@ -28917,7 +28995,7 @@ cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p)
   if (flag_concepts)
   {
     tree reqs = get_shorthand_constraints (current_template_parms);
-    if (tree treqs = cp_parser_requires_clause_opt (parser))
+    if (tree treqs = cp_parser_requires_clause_opt (parser, false))
       reqs = combine_constraint_expressions (reqs, treqs);
     TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
   }
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index af77dfa7c2b..257cec7148f 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2019-11-22  Andrew Sutton  <asutton@lock3software.com>
+
+	PR c++/92439
+	* g++.dg/cpp2a/concepts-requires20.C: New.
+
 2019-11-21  Wilco Dijkstra  <wdijkstr@arm.com>
 
 	* gfortran.dg/global_vars_f90_init_driver.c: Add missing extern.
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C
new file mode 100644
index 00000000000..089db2ba013
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C
@@ -0,0 +1,65 @@
+// { dg-do compile { target c++2a } }
+
+template<typename ...>
+constexpr bool r () { return true; }
+
+template<typename ... Ts>
+  requires r<Ts...>() // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires ++N // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N++ // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N == 0 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N ? true : false // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N = 0 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N + 1 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N - 1 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N.x // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N->x && true // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N && N
+void f() { }
+
+template<typename T, T N>
+  requires N || N
+void f() { }
+
+template<typename T, T N>
+  requires N || !N // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires N[0] // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+  requires static_cast<bool>(N) // { dg-error "enclose" }
+void f() { }
+
-- 
2.18.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] PR c++/92439: Improve diagnostics for ill-formed requires-clauses
  2019-11-22 16:08 [PATCH] PR c++/92439: Improve diagnostics for ill-formed requires-clauses Andrew Sutton
@ 2019-11-26 21:41 ` Jason Merrill
  0 siblings, 0 replies; 2+ messages in thread
From: Jason Merrill @ 2019-11-26 21:41 UTC (permalink / raw)
  To: Andrew Sutton, gcc-patches

On 11/22/19 10:28 AM, Andrew Sutton wrote:
> This does a much better job identifying when an expression in a
> requires-clause needs parentheses.
> 
> Andrew Sutton
> 
OK.

Jason

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2019-11-26 21:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-22 16:08 [PATCH] PR c++/92439: Improve diagnostics for ill-formed requires-clauses Andrew Sutton
2019-11-26 21:41 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).