Diagnose certain constraint errors as hard errors, but otherwise treat them the same...
authorAndrew Sutton <asutton@lock3software.com>
Wed, 27 Nov 2019 14:02:49 +0000 (14:02 +0000)
committerAndrew Sutton <asutton@gcc.gnu.org>
Wed, 27 Nov 2019 14:02:49 +0000 (14:02 +0000)
2019-11-27  Andrew Sutton  <asutton@lock3software.com>

Diagnose certain constraint errors as hard errors, but otherwise treat
them the same as normal SFINAE-type errors. Also, generally clean up
the satisfaction functions.

gcc/cp/
* constexpr.c (cxx_eval_constant_expression): Use
evaluate_concept_check.
* constraint.cc (normalize_concept_definition): Accept a diagnostic
flag and only cache when not diagnosing errors.
(decl_satisfied_cache): Map to trees instead of bools.
(satisfy_atom): Guarantee a location for the errors, propagate complain
flags to force_rvalue, and emit errors for non-boolean constraints.
(get_normalized_constraints_and_args): New overloads. Factored out of
satisfy_constraint_expression and satisfy_declaration_constraints.
(satisfy_constraint_expression): Propagate diagnostic info to
normalization.
(satisfy_declaration_constraints): New. Factored out of
constraints_satisfied_p.
(constraint_satisfaction_value): New. Calls
satisfy_constraint_expression or satisfy_declaration_constraints.
(constraints_satisfied_p): Call constraint_satisfaction_value.
(evaluate_concept_check): Don't take tsubst_falgs_t. Replay
satisfaction if an error is encountered.
(current_failed_constraint): Moved from pt.c.
(diagnose_constraints): Call constraint_satisfaction_value.
* cp-tree.h: Update declarations.
* pt.c (current_failed_constraint): Moved to constraint.cc.
* semantics.c (finish_id_expression_1): Remove a duplicate case.

gcc/testsuite/
* g++.dg/concepts/pr84330.C: Update diagnostics.
* g++.dg/cpp2a/concepts-requires2.C: Likewise.

From-SVN: r278768

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/constraint.cc
gcc/cp/cp-tree.h
gcc/cp/error.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/concepts/pr84330.C
gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C

index 4cbdb43..5f770e9 100644 (file)
@@ -1,3 +1,33 @@
+2019-11-27  Andrew Sutton  <asutton@lock3software.com>
+
+       Diagnose certain constraint errors as hard errors, but otherwise treat
+       them the same as normal SFINAE-type errors. Also, generally clean up
+       the satisfaction functions.
+
+       * constexpr.c (cxx_eval_constant_expression): Use
+       evaluate_concept_check.
+       * constraint.cc (normalize_concept_definition): Accept a diagnostic
+       flag and only cache when not diagnosing errors.
+       (decl_satisfied_cache): Map to trees instead of bools.
+       (satisfy_atom): Guarantee a location for the errors, propagate complain
+       flags to force_rvalue, and emit errors for non-boolean constraints.
+       (get_normalized_constraints_and_args): New overloads. Factored out of
+       satisfy_constraint_expression and satisfy_declaration_constraints.
+       (satisfy_constraint_expression): Propagate diagnostic info to
+       normalization.
+       (satisfy_declaration_constraints): New. Factored out of
+       constraints_satisfied_p.
+       (constraint_satisfaction_value): New. Calls
+       satisfy_constraint_expression or satisfy_declaration_constraints.
+       (constraints_satisfied_p): Call constraint_satisfaction_value.
+       (evaluate_concept_check): Don't take tsubst_falgs_t. Replay
+       satisfaction if an error is encountered.
+       (current_failed_constraint): Moved from pt.c.
+       (diagnose_constraints): Call constraint_satisfaction_value.
+       * cp-tree.h: Update declarations.
+       * pt.c (current_failed_constraint): Moved to constraint.cc.
+       * semantics.c (finish_id_expression_1): Remove a duplicate case.
+
 2019-11-27  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/92524
index f648b1d..32d929b 100644 (file)
@@ -5649,7 +5649,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          internal_error ("unexpected template-id %qE", t);
 
        if (!processing_template_decl)
-         return satisfy_constraint_expression (t);
+         return evaluate_concept_check (t, tf_warning_or_error);
        else
          *non_constant_p = true;
        return t;
index 171ca4e..d29c33a 100644 (file)
@@ -849,10 +849,12 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
 /* Returns the normal form of TMPL's definition.  */
 
 static tree
-normalize_concept_definition (tree tmpl)
+normalize_concept_definition (tree tmpl, bool diag = false)
 {
-  if (tree *p = hash_map_safe_get (normalized_map, tmpl))
-    return *p;
+  if (!diag)
+    if (tree *p = hash_map_safe_get (normalized_map, tmpl))
+      return *p;
+
   gcc_assert (concept_definition_p (tmpl));
   if (OVL_P (tmpl))
     tmpl = OVL_FIRST (tmpl);
@@ -860,10 +862,13 @@ normalize_concept_definition (tree tmpl)
   tree args = generic_targs_for (tmpl);
   tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
   ++processing_template_decl;
-  norm_info info (tmpl, tf_none);
+  norm_info info (tmpl, diag ? tf_norm : tf_none);
   tree norm = get_normalized_constraints (def, args, info);
   --processing_template_decl;
-  hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+
+  if (!diag)
+    hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+
   return norm;
 }
 
@@ -2231,8 +2236,8 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
 /* Cache the result of satisfy_atom.  */
 static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
 
-/* Cache the result of constraints_satisfied_p.  */
-static GTY((deletable)) hash_map<tree,bool> *decl_satisfied_cache;
+/* Cache the result of constraint_satisfaction_value.  */
+static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
 
 static tree
 get_satisfaction (tree constr, tree args)
@@ -2481,21 +2486,17 @@ satisfy_atom (tree t, tree args, subst_info info)
       return cache.save (boolean_false_node);
     }
 
-  location_t loc = cp_expr_location (expr);
+  location_t loc = cp_expr_loc_or_input_loc (expr);
 
   /* [17.4.1.2] ... lvalue-to-value conversion is performed as necessary,
      and EXPR shall be a constant expression of type bool.  */
-  result = force_rvalue (result, tf_error);
+  result = force_rvalue (result, info.complain);
   if (result == error_mark_node)
-    {
-      if (info.noisy ())
-        inform (loc, "cannot convert constraint to rvalue");
-      return cache.save (error_mark_node);
-    }
+    return cache.save (error_mark_node);
   if (!same_type_p (TREE_TYPE (result), boolean_type_node))
     {
       if (info.noisy ())
-       inform (loc, "constraint does not have type %<bool%>");
+       error_at (loc, "constraint does not have type %<bool%>");
       return cache.save (error_mark_node);
     }
 
@@ -2560,7 +2561,7 @@ satisfy_constraint (tree t, tree args, subst_info info)
 static tree
 satisfy_associated_constraints (tree t, tree args, subst_info info)
 {
-  /* If there are no constraints then this is trivially satisfied. */
+  /* If there are no constraints then this is trivially satisfied.  */
   if (!t)
     return boolean_true_node;
 
@@ -2576,24 +2577,31 @@ satisfy_associated_constraints (tree t, tree args, subst_info info)
    satisfaction value. */
 
 static tree
-satisfy_constraint_expression (tree expr, tree args, subst_info info)
+satisfy_constraint_expression (tree t, tree args, subst_info info)
 {
-  /* Normalize the expression before satisfaction testing.  */
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (EXPR_P (t));
+
+  /* Get the normalized constraints.  */
   tree norm;
-  if (args == NULL_TREE && concept_check_p (expr))
+  if (args == NULL_TREE && concept_check_p (t))
     {
-      tree id = unpack_concept_check (expr);
+      tree id = unpack_concept_check (t);
       args = TREE_OPERAND (id, 1);
       tree tmpl = get_concept_check_template (id);
-      norm = normalize_concept_definition (tmpl);
+      norm = normalize_concept_definition (tmpl, info.noisy ());
     }
   else
-    norm = normalize_constraint_expression (expr);
+    norm = normalize_constraint_expression (t, info.noisy ());
+
+  /* Perform satisfaction.  */
   return satisfy_constraint (norm, args, info);
 }
 
-/* Used to evaluate concept checks and requires-expressions during
-   constant expression evaluation.  */
+/* Used only to evaluate requires-expressions during constant expression
+   evaluation.  */
 
 tree
 satisfy_constraint_expression (tree expr)
@@ -2602,20 +2610,10 @@ satisfy_constraint_expression (tree expr)
   return satisfy_constraint_expression (expr, NULL_TREE, info);
 }
 
-/* True if T is satisfied for ARGS.  */
-
-static bool
-constraint_expression_satisfied_p (tree t, tree args, subst_info info)
-{
-  tree r = satisfy_constraint_expression (t, args, info);
-  return r == boolean_true_node;
-}
-
-static bool
-constraints_satisfied_p (tree t, subst_info info)
+static tree
+satisfy_declaration_constraints (tree t, subst_info info)
 {
-  if (!DECL_P (t))
-    return constraint_expression_satisfied_p (t, NULL_TREE, info);
+  gcc_assert (DECL_P (t));
 
   /* For inherited constructors, consider the original declaration;
      it has the correct template information attached. */
@@ -2626,21 +2624,19 @@ constraints_satisfied_p (tree t, subst_info info)
   info.in_decl = t;
 
   if (info.quiet ())
-    if (bool *p = hash_map_safe_get (decl_satisfied_cache, t))
-      return *p;
+    if (tree *result = hash_map_safe_get (decl_satisfied_cache, t))
+      return *result;
 
-  /* Get the constraints to check for satisfaction. This depends
-     on whether we're looking at a template specialization or not. */
+  /* Get the normalized constraints.  */
   tree norm = NULL_TREE;
   tree args = NULL_TREE;
-  tree ti = DECL_TEMPLATE_INFO (t);
-  if (ti)
+  if (tree ti = DECL_TEMPLATE_INFO (t))
     {
       tree tmpl = TI_TEMPLATE (ti);
       norm = normalize_template_requirements (tmpl, info.noisy ());
 
       /* The initial parameter mapping is the complete set of
-         template arguments substituted into the declaration.  */
+        template arguments substituted into the declaration.  */
       args = TI_ARGS (ti);
     }
   else
@@ -2649,44 +2645,23 @@ constraints_satisfied_p (tree t, subst_info info)
       norm = normalize_nontemplate_requirements (t, info.noisy ());
     }
 
-  bool r = true;
+  tree result = boolean_true_node;
   if (norm)
     {
       push_access_scope (t);
-      tree eval = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_associated_constraints (norm, args, info);
       pop_access_scope (t);
-      r = (eval == boolean_true_node);
     }
 
   if (info.quiet ())
-    hash_map_safe_put<hm_ggc> (decl_satisfied_cache, t, r);
-
-  return r;
-}
+    hash_map_safe_put<hm_ggc> (decl_satisfied_cache, t, result);
 
-/* Returns true if the T's constraints are satisfied, of if T is an expression,
-   if T is satisfied. This is used in cases where the arguments can be
-   determined from the declaration or expression.
-
-   Note that T is typically a template specialization.  */
-
-bool
-constraints_satisfied_p (tree t)
-{
-  subst_info info (tf_none, NULL_TREE);
-  return constraints_satisfied_p (t, info);
+  return result;
 }
 
-/* Returns true if the expression or constrained declaration T is
-   satisfied by ARGS.  In this case, we don't have a specialization
-   where we can cache the results (e.g., alias templates).  */
-
-static bool
-constraints_satisfied_p (tree t, tree args, subst_info info)
+static tree
+satisfy_declaration_constraints (tree t, tree args, subst_info info)
 {
-  if (!DECL_P (t))
-    return constraint_expression_satisfied_p (t, args, info);
-
   /* Update the declaration for diagnostics.  */
   info.in_decl = t;
 
@@ -2695,46 +2670,72 @@ constraints_satisfied_p (tree t, tree args, subst_info info)
     {
       tree pattern = DECL_TEMPLATE_RESULT (t);
       push_access_scope (pattern);
-      tree eval = satisfy_associated_constraints (norm, args, info);
+      tree result = satisfy_associated_constraints (norm, args, info);
       pop_access_scope (pattern);
-      return eval == boolean_true_node;
+      return result;
     }
 
-  return true;
+  return boolean_true_node;
 }
 
+static tree
+constraint_satisfaction_value (tree t, tsubst_flags_t complain)
+{
+  subst_info info (complain, NULL_TREE);
+  if (DECL_P (t))
+    return satisfy_declaration_constraints (t, info);
+  else
+    return satisfy_constraint_expression (t, NULL_TREE, info);
+}
+
+static tree
+constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain)
+{
+  subst_info info (complain, NULL_TREE);
+  if (DECL_P (t))
+    return satisfy_declaration_constraints (t, args, info);
+  else
+    return satisfy_constraint_expression (t, args, info);
+}
+
+/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
+   otherwise, even in the case of errors.  */
+
+bool
+constraints_satisfied_p (tree t)
+{
+  return constraint_satisfaction_value (t, tf_none) == boolean_true_node;
+}
+
+/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
+    and false otherwise, even in the case of errors.  */
+
 bool
 constraints_satisfied_p (tree t, tree args)
 {
-  subst_info info (tf_none, NULL);
-  return constraints_satisfied_p (t, args, info);
+  return constraint_satisfaction_value (t, args, tf_none) == boolean_true_node;
 }
 
-/* Evaluate a concept check of the form C<ARGS>, returning either TRUE
-   or FALSE. If ARGS contains any template parameters, this returns the
-   check. If satisfaction yields a hard error, diagnose the error.  */
+/* Evaluate a concept check of the form C<ARGS>. This is only used for the
+   evaluation of template-ids as id-expressions.  */
 
 tree
 evaluate_concept_check (tree check, tsubst_flags_t complain)
 {
-  /* FIXME we ought to be able to pass complain into subst_info rather
-     than repeat satisfaction, but currently that will complain about
-     non-satisfaction as well as errors.  */
   if (check == error_mark_node)
     return error_mark_node;
 
   gcc_assert (concept_check_p (check));
 
-  subst_info info (tf_none, NULL_TREE);
-  tree result = satisfy_constraint_expression (check, NULL_TREE, info);
+  /* Check for satisfaction without diagnostics.  */
+  subst_info quiet (tf_none, NULL_TREE);
+  tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
   if (result == error_mark_node && (complain & tf_error))
-    {
-      location_t loc = cp_expr_loc_or_input_loc (check);
-      error_at (loc, "concept satisfaction failed");
-      info.complain = complain;
-      satisfy_constraint_expression (check, NULL_TREE, info);
-    }
-
+  {
+    /* Replay the error with re-normalized requirements.  */
+    subst_info noisy (tf_warning_or_error, NULL_TREE);
+    satisfy_constraint_expression (check, NULL_TREE, noisy);
+  }
   return result;
 }
 
@@ -3275,6 +3276,8 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info)
     }
 }
 
+GTY(()) tree current_failed_constraint;
+
 diagnosing_failed_constraint::
 diagnosing_failed_constraint (tree t, tree args, bool diag)
   : diagnosing_error (diag)
@@ -3299,11 +3302,10 @@ diagnose_constraints (location_t loc, tree t, tree args)
   inform (loc, "constraints not satisfied");
 
   /* Replay satisfaction, but diagnose errors.  */
-  subst_info info (tf_warning_or_error, NULL_TREE);
   if (!args)
-    constraints_satisfied_p (t, info);
+    constraint_satisfaction_value (t, tf_warning_or_error);
   else
-    constraints_satisfied_p (t, args, info);
+    constraint_satisfaction_value (t, args, tf_warning_or_error);
 }
 
 #include "gt-cp-constraint.h"
index 0da1ed4..4b4bc24 100644 (file)
@@ -7821,8 +7821,8 @@ extern bool processing_constraint_expression_p    ();
 extern tree unpack_concept_check               (tree);
 extern tree evaluate_concept_check              (tree, tsubst_flags_t);
 extern tree satisfy_constraint_expression      (tree);
-extern bool constraints_satisfied_p             (tree);
-extern bool constraints_satisfied_p             (tree, tree);
+extern bool constraints_satisfied_p            (tree);
+extern bool constraints_satisfied_p            (tree, tree);
 extern void clear_satisfaction_cache           ();
 extern bool* lookup_subsumption_result          (tree, tree);
 extern bool save_subsumption_result             (tree, tree, bool);
index c06776f..4261d3c 100644 (file)
@@ -3358,6 +3358,9 @@ cp_print_error_function (diagnostic_context *context,
      to be wrong, so just rely on print_instantiation_full_context.  */
   if (current_instantiation ())
     return;
+  /* The above is true for constraint satisfaction also.  */
+  if (current_failed_constraint)
+    return;
   if (diagnostic_last_function_changed (context, diagnostic))
     {
       char *old_prefix = pp_take_prefix (context->printer);
index a11718e..eb907c5 100644 (file)
@@ -28736,8 +28736,6 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx)
   return tsubst (parm, replacement, tf_none, NULL_TREE);
 }
 
-GTY(()) tree current_failed_constraint;
-
 /* __integer_pack(N) in a pack expansion expands to a sequence of numbers from
    0..N-1.  */
 
index 16180f5..6c4785c 100644 (file)
@@ -3970,16 +3970,6 @@ finish_id_expression_1 (tree id_expression,
 
          decl = baselink_for_fns (decl);
        }
-      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);
-       }
       else
        {
          if (DECL_P (decl) && DECL_NONLOCAL (decl)
index 2fd0ae7..4c01d14 100644 (file)
@@ -1,3 +1,9 @@
+2019-11-27  Andrew Sutton  <asutton@lock3software.com>
+
+       Emit hard errors for certain satisfaction errors.
+       * g++.dg/concepts/pr84330.C: Update diagnostics.
+       * g++.dg/cpp2a/concepts-requires2.C: Likewise.
+
 2019-11-27  Richard Biener  <rguenther@suse.de>
 
        PR tree-optimization/92690
index 0c2f456..ba035d0 100644 (file)
@@ -5,7 +5,7 @@
 struct A
 {
   template<typename T>
-    requires (sizeof(T) >> 0)
+    requires (sizeof(T) >> 0) // { dg-error "constraint does not have type 'bool'" }
   void foo(T);
 
   void bar()
index 73cee22..8643f46 100644 (file)
@@ -12,7 +12,7 @@ template<typename T> constexpr fool p1() { return {}; }
 template<typename T> constexpr fool p2() { return {}; }
 
 template<typename T>
-concept Bad = p1<T>() && p2<T>();
+concept Bad = p1<T>() && p2<T>(); // { dg-error "does not have type 'bool'" }
 
 template<typename T> requires Bad<T> void bad(T x) { }
 
@@ -26,14 +26,14 @@ struct X { };
 int operator==(X, X) { return 0; }
 
 template<typename T>
-concept C1 = (X());
+concept C1 = (X()); // { dg-error "does not have type 'bool'" }
 
 template<typename T>
-concept C2 = (X() == X());
+concept C2 = (X() == X()); // { dg-error "does not have type 'bool'" }
 
 template<typename T>
   requires C1<T>
-void h1(T) { } 
+void h1(T) { }
 
 template<typename T>
   requires C2<T>
@@ -42,12 +42,12 @@ void h2(T);
 void driver_3()
 {
   h1(0); // { dg-error "" }
-  h2(0); // { dg-error "" } 
+  h2(0); // { dg-error "" }
 }
 
 // req7.C
 template<bool B>
-struct boolean_constant 
+struct boolean_constant
 {
   constexpr operator bool() const { return B; }
 };