re PR c++/92236 ([concepts] Explain non-satisfaction in static_assert)
authorAndrew Sutton <asutton@lock3software.com>
Wed, 27 Nov 2019 15:23:02 +0000 (15:23 +0000)
committerAndrew Sutton <asutton@gcc.gnu.org>
Wed, 27 Nov 2019 15:23:02 +0000 (15:23 +0000)
2019-11-27  Andrew Sutton  <asutton@lock3software.com>

PR c++/92236
Defer evaluation of concept checks so that static assertions can
emit more detailed diagnostics.

gcc/cp/
* constexpr.c (cxx_eval_call_expression): Handle concept checks.
(cxx_eval_constant_expression): Diagnose misuse of function concepts
as template-id expressions. Follow the usual return path for results.
(cxx_eval_outermost_constant_expr): Avoid calling
cp_get_callee_fndecl_nofold for function concepts.
* constraint.cc (build_function_check): Fully type the concept check
so that we don't ICE in conversions.
* cp-gimplify.c (cp_genericize_r) [CALL_EXPR]: Handle concept checks.
[TEMPLATE_ID_EXPR] Likewise.
* cvt.c (convert_to_void): Always evaluate concept checks so we don't
accidentally ignore them. Substitution during satisfaction can make
a program ill-formed (example in g++.dg/cpp2a/concepts6.C).
* pt.c (tsubst_copy_and_build): [CALL_EXPR]: Don't evaluate concepts.
[TEMPLATE_ID_EXPR]: Likewise.
* semantics.c (finish_call_expr): Don't evaluate concepts.
(finish_id_expression_1): Likewise.
(finish_static_assert): Preserve the original condition so we can
diagnose concept errors when a check returns false.

gcc/testsuite/
* g++.dg/cpp2a/concepts-iconv1.C: Update diagnostics.
* g++.dg/cpp2a/concepts-requires5.C: Likewise.
* g++.dg/cpp2a/concepts6.C: New test.

From-SVN: r278775

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/constraint.cc
gcc/cp/cp-gimplify.c
gcc/cp/cvt.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C
gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C

index 36302e43aca7995645a621e836a5a352365cde49..c3e66d4e40fb58bf26a4115defe932b8e398e392 100644 (file)
@@ -1,3 +1,27 @@
+2019-11-27  Andrew Sutton  <asutton@lock3software.com>
+
+       PR c++/92236
+       Defer evaluation of concept checks so that static assertions can
+       emit more detailed diagnostics.
+       * constexpr.c (cxx_eval_call_expression): Handle concept checks.
+       (cxx_eval_constant_expression): Diagnose misuse of function concepts
+       as template-id expressions. Follow the usual return path for results.
+       (cxx_eval_outermost_constant_expr): Avoid calling
+       cp_get_callee_fndecl_nofold for function concepts.
+       * constraint.cc (build_function_check): Fully type the concept check
+       so that we don't ICE in conversions.
+       * cp-gimplify.c (cp_genericize_r) [CALL_EXPR]: Handle concept checks.
+       [TEMPLATE_ID_EXPR] Likewise.
+       * cvt.c (convert_to_void): Always evaluate concept checks so we don't
+       accidentally ignore them. Substitution during satisfaction can make
+       a program ill-formed (example in g++.dg/cpp2a/concepts6.C).
+       * pt.c (tsubst_copy_and_build): [CALL_EXPR]: Don't evaluate concepts.
+       [TEMPLATE_ID_EXPR]: Likewise.
+       * semantics.c (finish_call_expr): Don't evaluate concepts.
+       (finish_id_expression_1): Likewise.
+       (finish_static_assert): Preserve the original condition so we can
+       diagnose concept errors when a check returns false.
+
 2019-11-27  Andrew Sutton  <asutton@lock3software.com>
 
        PR c++/92439
index 32d929b82f3d489dd42854e5d4fbf4ad32246aea..ee3ccb9691cafe20dc61e7ec9c3364a8dc71ab80 100644 (file)
@@ -1672,6 +1672,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
                          bool lval,
                          bool *non_constant_p, bool *overflow_p)
 {
+  /* Handle concept checks separately.  */
+  if (concept_check_p (t))
+    return evaluate_concept_check (t, tf_warning_or_error);
+
   location_t loc = cp_expr_loc_or_input_loc (t);
   tree fun = get_function_named_in_call (t);
   constexpr_call new_call
@@ -5645,14 +5649,26 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       {
         /* We can evaluate template-id that refers to a concept only if
           the template arguments are non-dependent.  */
-       if (!concept_definition_p (TREE_OPERAND (t, 0)))
+       tree id = unpack_concept_check (t);
+       tree tmpl = TREE_OPERAND (id, 0);
+       if (!concept_definition_p (tmpl))
          internal_error ("unexpected template-id %qE", t);
 
+       if (function_concept_p (tmpl))
+         {
+           if (!ctx->quiet)
+             error_at (cp_expr_loc_or_input_loc (t),
+                       "function concept must be called");
+           r = error_mark_node;
+           break;
+         }
+
        if (!processing_template_decl)
-         return evaluate_concept_check (t, tf_warning_or_error);
+         r = evaluate_concept_check (t, tf_warning_or_error);
        else
          *non_constant_p = true;
-       return t;
+
+       break;
       }
 
     case ASM_EXPR:
@@ -5809,12 +5825,16 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
               || TREE_CODE (t) == AGGR_INIT_EXPR
               || TREE_CODE (t) == TARGET_EXPR))
     {
-      tree x = t;
-      if (TREE_CODE (x) == TARGET_EXPR)
-       x = TARGET_EXPR_INITIAL (x);
-      tree fndecl = cp_get_callee_fndecl_nofold (x);
-      if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl))
-       is_consteval = true;
+      /* For non-concept checks, determine if it is consteval.  */
+      if (!concept_check_p (t))
+       {
+         tree x = t;
+         if (TREE_CODE (x) == TARGET_EXPR)
+           x = TARGET_EXPR_INITIAL (x);
+         tree fndecl = cp_get_callee_fndecl_nofold (x);
+         if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl))
+           is_consteval = true;
+       }
     }
   if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
     {
index 9967b1ef996993d6741c925cc89d8d0ba9fe6ea8..fadbe7c8ac0a221662be4e24c3d45a13f0cbe6de 100644 (file)
@@ -1263,6 +1263,7 @@ build_function_check (tree tmpl, tree args, tsubst_flags_t /*complain*/)
   ++processing_template_decl;
   vec<tree, va_gc> *fargs = make_tree_vector ();
   tree call = build_min_nt_call_vec (id, fargs);
+  TREE_TYPE (call) = boolean_type_node;
   release_tree_vector (fargs);
   --processing_template_decl;
 
@@ -1397,6 +1398,7 @@ build_constrained_parameter (tree cnc, tree proto, tree args)
 
    Note that the constraints are neither reduced nor decomposed. That is
    done only after the requires clause has been parsed (or not).  */
+
 tree
 finish_shorthand_constraint (tree decl, tree constr)
 {
index 60766978c71858d8ce3105ed12eeae2a5d7d71d6..7942afa7ecead609f6114574d93bf5f80f4bc317 100644 (file)
@@ -1622,6 +1622,15 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       break;
 
     case CALL_EXPR:
+      /* Evaluate function concept checks instead of treating them as
+        normal functions.  */
+      if (concept_check_p (stmt))
+       {
+         *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+         * walk_subtrees = 0;
+         break;
+       }
+
       if (!wtd->no_sanitize_p
          && sanitize_flags_p ((SANITIZE_NULL
                                | SANITIZE_ALIGNMENT | SANITIZE_VPTR)))
@@ -1679,6 +1688,13 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
        TARGET_EXPR_NO_ELIDE (stmt) = 1;
       break;
 
+    case TEMPLATE_ID_EXPR:
+      gcc_assert (concept_check_p (stmt));
+      /* Emit the value of the concept check.  */
+      *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+      walk_subtrees = 0;
+      break;
+
     default:
       if (IS_TYPE_OR_DECL_P (stmt))
        *walk_subtrees = 0;
index e67b32ea3b0de6fd6b6f7894d1184636c96116f2..d916e39ee9035cd1e420b9bf41b8e13e731b9bec 100644 (file)
@@ -1135,6 +1135,12 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
         error_at (loc, "pseudo-destructor is not called");
       return error_mark_node;
     }
+
+  /* Explicitly evaluate void-converted concept checks since their
+     satisfaction may produce ill-formed programs.  */
+   if (concept_check_p (expr))
+     expr = evaluate_concept_check (expr, tf_warning_or_error);
+
   if (VOID_TYPE_P (TREE_TYPE (expr)))
     return expr;
   switch (TREE_CODE (expr))
index eb907c5638502bb942badf17e4e8d87bcf3e1165..3eed27bb4265c9a4d3b5fec0e20c98dec45b0a14 100644 (file)
@@ -18860,12 +18860,6 @@ tsubst_copy_and_build (tree t,
            if (function_concept_p (TREE_OPERAND (id, 0)))
              RETURN (id);
 
-           /* Evaluate the concept, if needed.  */
-           tree args = TREE_OPERAND (id, 1);
-           if (!uses_template_parms (args)
-               && !processing_constraint_expression_p ())
-             RETURN (evaluate_concept_check (check, complain));
-
            RETURN (check);
          }
 
@@ -19650,11 +19644,6 @@ tsubst_copy_and_build (tree t,
 
            /* Ensure the result is wrapped as a call expression.  */
            ret = build_concept_check (tmpl, args, tf_warning_or_error);
-
-           /* Possibly evaluate the check if it is non-dependent.   */
-           if (!uses_template_parms (args)
-               && !processing_constraint_expression_p ())
-             ret = evaluate_concept_check (ret, complain);
          }
        else
          ret = finish_call_expr (function, &call_args,
index 6c4785c6858df154a9d25358f1f7b53f9d482b90..4a5479cb4b4eba13330a844d0b848c672d672808 100644 (file)
@@ -2605,10 +2605,6 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 
       /* Ensure the result is wrapped as a call expression.  */
       result = build_concept_check (tmpl, args, tf_warning_or_error);
-
-      /* Evaluate the check if it is non-dependent.   */
-      if (!uses_template_parms (args))
-       result = evaluate_concept_check (result, complain);
     }
   else if (is_overloaded_fn (fn))
     {
@@ -3890,13 +3886,8 @@ finish_id_expression_1 (tree id_expression,
        }
       else if (concept_check_p (decl))
        {
-         /* If this is a standard or variable concept check, potentially
-            evaluate it. Function concepts need to be called as functions,
-            so don't try evaluating them here.  */
-         tree tmpl = TREE_OPERAND (decl, 0);
-         tree args = TREE_OPERAND (decl, 1);
-         if (!function_concept_p (tmpl) && !uses_template_parms (args))
-           decl = evaluate_concept_check (decl, tf_warning_or_error);
+         /* Nothing more to do. All of the analysis for concept checks
+            is done by build_conept_id, called from the parser.  */
        }
       else if (scope)
        {
@@ -9564,6 +9555,9 @@ finish_static_assert (tree condition, tree message, location_t location,
       return;
     }
 
+  /* Save the condition in case it was a concept check.  */
+  tree orig_condition = condition;
+
   /* Fold the expression and convert it to a boolean value. */
   condition = perform_implicit_conversion_flags (boolean_type_node, condition,
                                                 complain, LOOKUP_NORMAL);
@@ -9590,6 +9584,10 @@ finish_static_assert (tree condition, tree message, location_t location,
          else
             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))
+           diagnose_constraints (location, orig_condition, NULL_TREE);
        }
       else if (condition && condition != error_mark_node)
        {
index 9b22c91ce69bf1d752e58f9419fd26ad9ca03f3e..aca3864b184d6d0e45880f4409378bad20fe5d3f 100644 (file)
@@ -1,3 +1,10 @@
+2019-11-27  Andrew Sutton  <asutton@lock3software.com>
+
+       PR c++/92236
+       * g++.dg/cpp2a/concepts-iconv1.C: Update diagnostics.
+       * g++.dg/cpp2a/concepts-requires5.C: Likewise.
+       * g++.dg/cpp2a/concepts6.C: New test.
+
 2019-11-27  Andrew Sutton  <asutton@lock3software.com>
 
        PR c++/92439
index cc2ce7e321d82ed8d2f90445cb1179d76d5a0b01..4d521c30748f6b31cb2d58bfa156d34d08183ddc 100644 (file)
@@ -7,16 +7,16 @@ int foo(int x)
 {
     return x;
 }
+
 template <typename T>
 concept C1 = requires (T x) {
-    {foo(x)} -> Same<int&>;
+    {foo(x)} -> Same<int&>; // { dg-error "placeholder constraints" }
 };
 
 template <typename T>
 concept C2 = requires (T x) {
-    {foo(x)} -> Same<void>;
+    {foo(x)} -> Same<void>; // { dg-error "placeholder constraints" }
 };
+
 static_assert( C1<int> );      // { dg-error "assert" }
 static_assert( C2<int> );      // { dg-error "assert" }
index fe37ed48e79c20897c251edb9d69ef315a9bd8e8..133d29e45a439b5ca3c676d00caefd6d7e30b38f 100644 (file)
@@ -33,13 +33,13 @@ int driver_1()
 // Test implicit conversion requirements
 
 template<typename T, typename U>
-concept ConvertibleTo = requires(T& t) { {t} -> U&; };
+concept ConvertibleTo = requires(T& t) { {t} -> U&; }; // { dg-error "inaccessible" }
 
 struct B { };
 class D : /*private*/ B { };
 
 void driver_2()
 {
-  static_assert(ConvertibleTo<D, B>()); // { dg-error "cannot be used as a function" }
+  static_assert(ConvertibleTo<D, B>()); // { dg-error "cannot call" }
   static_assert(ConvertibleTo<D, B>); // { dg-error "static assertion failed" }
 }