c++: requires-expression outside of a template is misevaluated [PR94252]
authorPatrick Palka <ppalka@redhat.com>
Sat, 28 Mar 2020 12:56:59 +0000 (08:56 -0400)
committerPatrick Palka <ppalka@redhat.com>
Sat, 28 Mar 2020 12:56:59 +0000 (08:56 -0400)
This PR shows that a REQUIRES_EXPR outside of a template can sometimes be
misevaluated.  This happens because the evaluation routine tsubst_requires_expr
(and diagnose_requires_expr) assumes the REQUIRES_EXPR's subtrees are templated
trees and that therefore it's safe to call tsubst_expr on them.  But this
assumption isn't valid when we've parsed a REQUIRES_EXPR outside of a template
context.  In order to make this assumption valid here, this patch sets
processing_template_decl to non-zero before parsing the body of a REQUIRES_EXPR
so that its subtrees are indeed always templated trees.

gcc/cp/ChangeLog:

PR c++/94252
* constraint.cc (tsubst_compound_requirement): Always suppress errors
from type_deducible_p and expression_convertible_p, as they're not
substitution errors.
(diagnose_atomic_constraint) <case INTEGER_CST>: Remove this case so
that we diagnose INTEGER_CST expressions of non-bool type via the
default case.
* cp-gimplify.c (cp_genericize_r) <case REQUIRES_EXPR>: New case.
* parser.c (cp_parser_requires_expression): Always parse the requirement
body as if we're processing a template, by temporarily incrementing
processing_template_decl.  Afterwards, if we're not actually in a
template context, perform semantic processing to diagnose any invalid
types and expressions.
* pt.c (tsubst_copy_and_build) <case REQUIRES_EXPR>: Remove dead code.
* semantics.c (finish_static_assert): Explain an assertion failure
when the condition is a REQUIRES_EXPR like we do when it is a concept
check.

gcc/testsuite/ChangeLog:

PR c++/94252
* g++.dg/concepts/diagnostic7.C: New test.
* g++.dg/concepts/pr94252.C: New test.
* g++.dg/cpp2a/concepts-requires18.C: Adjust to expect an additional
diagnostic.

gcc/cp/ChangeLog
gcc/cp/constraint.cc
gcc/cp/cp-gimplify.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/concepts/diagnostic7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr94252.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C

index be67685d0c2d20e691fc2fe79f364cbd5c7184a1..a197c7d14e230966858012deb82681c8bf1bac43 100644 (file)
@@ -1,5 +1,23 @@
 2020-03-28  Patrick Palka  <ppalka@redhat.com>
 
+       PR c++/94252
+       * constraint.cc (tsubst_compound_requirement): Always suppress errors
+       from type_deducible_p and expression_convertible_p, as they're not
+       substitution errors.
+       (diagnose_atomic_constraint) <case INTEGER_CST>: Remove this case so
+       that we diagnose INTEGER_CST expressions of non-bool type via the
+       default case.
+       * cp-gimplify.c (cp_genericize_r) <case REQUIRES_EXPR>: New case.
+       * parser.c (cp_parser_requires_expression): Always parse the requirement
+       body as if we're processing a template, by temporarily incrementing
+       processing_template_decl.  Afterwards, if we're not actually in a
+       template context, perform semantic processing to diagnose any invalid
+       types and expressions.
+       * pt.c (tsubst_copy_and_build) <case REQUIRES_EXPR>: Remove dead code.
+       * semantics.c (finish_static_assert): Explain an assertion failure
+       when the condition is a REQUIRES_EXPR like we do when it is a concept
+       check.
+
        * constraint.cc (diagnose_compound_requirement): When diagnosing a
        compound requirement, maybe replay the satisfaction failure, subject to
        the current diagnosis depth.
index 571c7cbdd38bf217a771044da0f3be5dfe279b5c..9c21ce80256d6a638f4bcc48e5c966c143bc66d7 100644 (file)
@@ -1981,15 +1981,17 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
   if (type == error_mark_node)
     return error_mark_node;
 
+  subst_info quiet (tf_none, info.in_decl);
+
   /* Check expression against the result type.  */
   if (type)
     {
       if (tree placeholder = type_uses_auto (type))
        {
-         if (!type_deducible_p (expr, type, placeholder, args, info))
+         if (!type_deducible_p (expr, type, placeholder, args, quiet))
            return error_mark_node;
        }
-      else if (!expression_convertible_p (expr, type, info))
+      else if (!expression_convertible_p (expr, type, quiet))
        return error_mark_node;
     }
 
@@ -3443,10 +3445,6 @@ diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info)
     case REQUIRES_EXPR:
       diagnose_requires_expr (expr, map, info.in_decl);
       break;
-    case INTEGER_CST:
-      /* This must be either 0 or false.  */
-      inform (loc, "%qE is never satisfied", expr);
-      break;
     default:
       tree a = copy_node (t);
       ATOMIC_CONSTR_MAP (a) = map;
index d003f5b7825c5ae1cd99bf9a2c12990363f907ec..3999695ae93ea6c98dfd9d2f8fa4b3bb70c7f2a6 100644 (file)
@@ -1747,6 +1747,13 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
        TARGET_EXPR_NO_ELIDE (stmt) = 1;
       break;
 
+    case REQUIRES_EXPR:
+      /* Emit the value of the requires-expression.  */
+      *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt),
+                                      boolean_type_node);
+      *walk_subtrees = 0;
+      break;
+
     case TEMPLATE_ID_EXPR:
       gcc_assert (concept_check_p (stmt));
       /* Emit the value of the concept check.  */
index 3ca8eb9baf8b5249ec107f88fbe88b9e9f33f33d..bf7387dd1495003e6e8e3e2e9f45a1df6c61411c 100644 (file)
@@ -27762,7 +27762,9 @@ cp_parser_requires_expression (cp_parser *parser)
       parms = NULL_TREE;
 
     /* Parse the requirement body. */
+    ++processing_template_decl;
     reqs = cp_parser_requirement_body (parser);
+    --processing_template_decl;
     if (reqs == error_mark_node)
       return error_mark_node;
   }
@@ -27771,7 +27773,17 @@ cp_parser_requires_expression (cp_parser *parser)
      the parm chain.  */
   grokparms (parms, &parms);
   loc = make_location (loc, loc, parser->lexer);
-  return finish_requires_expr (loc, parms, reqs);
+  tree expr = finish_requires_expr (loc, parms, reqs);
+  if (!processing_template_decl)
+    {
+      /* Perform semantic processing now to diagnose any invalid types and
+        expressions.  */
+      int saved_errorcount = errorcount;
+      tsubst_requires_expr (expr, NULL_TREE, tf_warning_or_error, NULL_TREE);
+      if (errorcount > saved_errorcount)
+       return error_mark_node;
+    }
+  return expr;
 }
 
 /* Parse a parameterized requirement.
index 15b54396ed3103d941038a2e74ce623be6a73054..bd30c96a12a1876b2901baccb355d62aa095b9ae 100644 (file)
@@ -20319,8 +20319,6 @@ tsubst_copy_and_build (tree t,
     case REQUIRES_EXPR:
       {
        tree r = tsubst_requires_expr (t, args, tf_none, in_decl);
-       if (r == error_mark_node && (complain & tf_error))
-         tsubst_requires_expr (t, args, complain, in_decl);
        RETURN (r);
       }
 
index 2721a55c982e2a9ee1ff171f4b77bd7ae9075c53..38637bdf3f365774cdf7cd469a82052f4998f2f6 100644 (file)
@@ -9687,8 +9687,10 @@ finish_static_assert (tree condition, tree message, location_t location,
             error ("static assertion failed: %s",
                   TREE_STRING_POINTER (message));
 
-         /* Actually explain the failure if this is a concept check.  */
-         if (concept_check_p (orig_condition))
+         /* Actually explain the failure if this is a concept check or a
+            requires-expression.  */
+         if (concept_check_p (orig_condition)
+             || TREE_CODE (orig_condition) == REQUIRES_EXPR)
            diagnose_constraints (location, orig_condition, NULL_TREE);
        }
       else if (condition && condition != error_mark_node)
index e2e00f4c2b43a93028dbc83976c7fab4d29799d1..8c8eee014fd7f8b6315c17bf79dbeab651872481 100644 (file)
@@ -1,5 +1,11 @@
 2020-03-28  Patrick Palka  <ppalka@redhat.com>
 
+       PR c++/94252
+       * g++.dg/concepts/diagnostic7.C: New test.
+       * g++.dg/concepts/pr94252.C: New test.
+       * g++.dg/cpp2a/concepts-requires18.C: Adjust to expect an additional
+       diagnostic.
+
        * g++.dg/concepts/diagnostic1.C: Pass -fconcepts-diagnostics-depth=2.
        * g++.dg/concepts/diagnostic5.C: Adjust expected diagnostics.
        * g++.dg/cpp2a/concepts-iconv1.C: Pass -fconcepts-diagnostics-depth=2.
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic7.C b/gcc/testsuite/g++.dg/concepts/diagnostic7.C
new file mode 100644 (file)
index 0000000..3761b2b
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++2a } }
+
+template<typename A, typename B>
+  concept same_as = __is_same(A, B);
+
+void f();
+
+static_assert(requires { { f() } noexcept -> same_as<int>; });
+// { dg-error "static assertion failed" "" { target *-*-* } .-1 }
+// { dg-message "not .noexcept." "" { target *-*-* } .-2 }
+// { dg-message "return-type-requirement" "" { target *-*-* } .-3 }
diff --git a/gcc/testsuite/g++.dg/concepts/pr94252.C b/gcc/testsuite/g++.dg/concepts/pr94252.C
new file mode 100644 (file)
index 0000000..56ce5f8
--- /dev/null
@@ -0,0 +1,27 @@
+// PR c++/94252
+// { dg-do compile { target c++2a } }
+
+auto f = []{ return 0; };
+static_assert(requires { f(); });
+static_assert(requires { requires requires { f(); }; });
+
+template<typename A, typename B>
+  concept same_as = __is_same(A, B);
+
+struct S { int f(int) noexcept; };
+static_assert(requires(S o, int i) {
+  o.f(i);
+  { o.f(i) } noexcept -> same_as<int>;
+});
+
+template<typename T>
+  concept c = requires (T t) { requires (T)5; }; // { dg-error "has type .int." }
+
+int
+foo()
+{
+  requires { requires c<int>; };
+  requires { { 5 } -> same_as<bool>; };
+  requires { requires !requires { { 5 } -> same_as<bool>; }; };
+  return requires { requires 5; }; // { dg-error "has type .int." }
+}
index c76b12c6414681334d2890ae38f3d5da1ac97876..c97704565a1bd9186333e741601c6022b1c340b1 100644 (file)
@@ -4,7 +4,7 @@ template<typename T>
 concept integer = __is_same_as(T, int);
 
 template<typename T>
-concept subst = requires (T x) { requires true; };
+concept subst = requires (T x) { requires true; }; // { dg-error "parameter type .void." }
 
 template<typename T>
 concept c1 = requires { requires integer<T> || subst<T&>; }; // { dg-message "in requirements" }