diff gcc/cp/constraint.cc @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/cp/constraint.cc	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,3146 @@
+/* Processing rules for constraints.
+   Copyright (C) 2013-2017 Free Software Foundation, Inc.
+   Contributed by Andrew Sutton (andrew.n.sutton@gmail.com)
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "timevar.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "intl.h"
+#include "flags.h"
+#include "cp-tree.h"
+#include "c-family/c-common.h"
+#include "c-family/c-objc.h"
+#include "cp-objcp-common.h"
+#include "tree-inline.h"
+#include "decl.h"
+#include "toplev.h"
+#include "type-utils.h"
+
+/*---------------------------------------------------------------------------
+                       Operations on constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true if C is a constraint tree code. Note that ERROR_MARK
+   is a valid constraint.  */
+
+static inline bool
+constraint_p (tree_code c)
+{
+  return ((PRED_CONSTR <= c && c <= DISJ_CONSTR)
+          || c == EXPR_PACK_EXPANSION
+          || c == ERROR_MARK);
+}
+
+/* Returns true if T is a constraint. Note that error_mark_node
+   is a valid constraint.  */
+
+bool
+constraint_p (tree t)
+{
+  return constraint_p (TREE_CODE (t));
+}
+
+/* Returns the conjunction of two constraints A and B. Note that
+   conjoining a non-null constraint with NULL_TREE is an identity
+   operation. That is, for non-null A,
+
+      conjoin_constraints(a, NULL_TREE) == a
+
+   and
+
+      conjoin_constraints (NULL_TREE, a) == a
+
+   If both A and B are NULL_TREE, the result is also NULL_TREE. */
+
+tree
+conjoin_constraints (tree a, tree b)
+{
+  gcc_assert (a ? constraint_p (a) : true);
+  gcc_assert (b ? constraint_p (b) : true);
+  if (a)
+    return b ? build_nt (CONJ_CONSTR, a, b) : a;
+  else if (b)
+    return b;
+  else
+    return NULL_TREE;
+}
+
+/* Transform the vector of expressions in the T into a conjunction
+   of requirements. T must be a TREE_VEC. */
+
+tree
+conjoin_constraints (tree t)
+{
+  gcc_assert (TREE_CODE (t) == TREE_VEC);
+  tree r = NULL_TREE;
+  for (int i = 0; i < TREE_VEC_LENGTH (t); ++i)
+    r = conjoin_constraints (r, TREE_VEC_ELT (t, i));
+  return r;
+}
+
+/* Returns true if T is a call expression to a function
+   concept. */
+
+bool
+function_concept_check_p (tree t)
+{
+  gcc_assert (TREE_CODE (t) == CALL_EXPR);
+  tree fn = CALL_EXPR_FN (t);
+  if (fn != NULL_TREE
+      && TREE_CODE (fn) == TEMPLATE_ID_EXPR)
+    {
+      tree f1 = OVL_FIRST (TREE_OPERAND (fn, 0));
+      if (TREE_CODE (f1) == TEMPLATE_DECL
+	  && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (f1)))
+        return true;
+    }
+  return false;
+}
+
+/* Returns true if any of the arguments in the template
+   argument list is a wildcard or wildcard pack.  */
+
+bool
+contains_wildcard_p (tree args)
+{
+  for (int i = 0; i < TREE_VEC_LENGTH (args); ++i)
+    {
+      tree arg = TREE_VEC_ELT (args, i);
+      if (TREE_CODE (arg) == WILDCARD_DECL)
+	return true;
+    }
+  return false;
+}
+
+/* Build a new call expression, but don't actually generate a
+   new function call. We just want the tree, not the semantics.  */
+
+inline tree
+build_call_check (tree id)
+{
+  ++processing_template_decl;
+  vec<tree, va_gc> *fargs = make_tree_vector();
+  tree call = finish_call_expr (id, &fargs, false, false, tf_none);
+  release_tree_vector (fargs);
+  --processing_template_decl;
+  return call;
+}
+
+/* Build an expression that will check a variable concept. If any
+   argument contains a wildcard, don't try to finish the variable
+   template because we can't substitute into a non-existent
+   declaration.  */
+
+tree
+build_variable_check (tree id)
+{
+  gcc_assert (TREE_CODE (id) == TEMPLATE_ID_EXPR);
+  if (contains_wildcard_p (TREE_OPERAND (id, 1)))
+    return id;
+
+  ++processing_template_decl;
+  tree var = finish_template_variable (id);
+  --processing_template_decl;
+  return var;
+}
+
+/*---------------------------------------------------------------------------
+                    Resolution of qualified concept names
+---------------------------------------------------------------------------*/
+
+/* This facility is used to resolve constraint checks from
+   requirement expressions. A constraint check is a call to
+   a function template declared with the keyword 'concept'.
+
+   The result of resolution is a pair (a TREE_LIST) whose value
+   is the matched declaration, and whose purpose contains the
+   coerced template arguments that can be substituted into the
+   call.  */
+
+// Given an overload set OVL, try to find a unique definition that can be
+// instantiated by the template arguments ARGS.
+//
+// This function is not called for arbitrary call expressions. In particular,
+// the call expression must be written with explicit template arguments
+// and no function arguments. For example:
+//
+//      f<T, U>()
+//
+// If a single match is found, this returns a TREE_LIST whose VALUE
+// is the constraint function (not the template), and its PURPOSE is
+// the complete set of arguments substituted into the parameter list.
+static tree
+resolve_constraint_check (tree ovl, tree args)
+{
+  int nerrs = 0;
+  tree cands = NULL_TREE;
+  for (lkp_iterator iter (ovl); iter; ++iter)
+    {
+      // Get the next template overload.
+      tree tmpl = *iter;
+      if (TREE_CODE (tmpl) != TEMPLATE_DECL)
+        continue;
+
+      // Don't try to deduce checks for non-concepts. We often
+      // end up trying to resolve constraints in functional casts
+      // as part of a postfix-expression. We can save time and
+      // headaches by not instantiating those declarations.
+      //
+      // NOTE: This masks a potential error, caused by instantiating
+      // non-deduced contexts using placeholder arguments.
+      tree fn = DECL_TEMPLATE_RESULT (tmpl);
+      if (DECL_ARGUMENTS (fn))
+        continue;
+      if (!DECL_DECLARED_CONCEPT_P (fn))
+        continue;
+
+      // Remember the candidate if we can deduce a substitution.
+      ++processing_template_decl;
+      tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
+      if (tree subst = coerce_template_parms (parms, args, tmpl))
+        {
+          if (subst == error_mark_node)
+            ++nerrs;
+          else
+	    cands = tree_cons (subst, fn, cands);
+        }
+      --processing_template_decl;
+    }
+
+  if (!cands)
+    /* We either had no candidates or failed deductions.  */
+    return nerrs ? error_mark_node : NULL_TREE;
+  else if (TREE_CHAIN (cands))
+    /* There are multiple candidates.  */
+    return error_mark_node;
+
+  return cands;
+}
+
+// Determine if the the call expression CALL is a constraint check, and
+// return the concept declaration and arguments being checked. If CALL
+// does not denote a constraint check, return NULL.
+tree
+resolve_constraint_check (tree call)
+{
+  gcc_assert (TREE_CODE (call) == CALL_EXPR);
+
+  // A constraint check must be only a template-id expression. If
+  // it's a call to a base-link, its function(s) should be a
+  // template-id expression. If this is not a template-id, then it
+  // cannot be a concept-check.
+  tree target = CALL_EXPR_FN (call);
+  if (BASELINK_P (target))
+    target = BASELINK_FUNCTIONS (target);
+  if (TREE_CODE (target) != TEMPLATE_ID_EXPR)
+    return NULL_TREE;
+
+  // Get the overload set and template arguments and try to
+  // resolve the target.
+  tree ovl = TREE_OPERAND (target, 0);
+
+  /* This is a function call of a variable concept... ill-formed. */
+  if (TREE_CODE (ovl) == TEMPLATE_DECL)
+    {
+      error_at (location_of (call),
+		"function call of variable concept %qE", call);
+      return error_mark_node;
+    }
+
+  tree args = TREE_OPERAND (target, 1);
+  return resolve_constraint_check (ovl, args);
+}
+
+/* Returns a pair containing the checked variable concept
+   and its associated prototype parameter.  The result
+   is a TREE_LIST whose TREE_VALUE is the variable concept
+   and whose TREE_PURPOSE is the prototype parameter.  */
+
+tree
+resolve_variable_concept_check (tree id)
+{
+  tree tmpl = TREE_OPERAND (id, 0);
+  tree args = TREE_OPERAND (id, 1);
+
+  if (!variable_concept_p (tmpl))
+    return NULL_TREE;
+
+  /* Make sure that we have the right parameters before
+     assuming that it works.  Note that failing to deduce
+     will result in diagnostics.  */
+  tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
+  ++processing_template_decl;
+  tree result = coerce_template_parms (parms, args, tmpl);
+  --processing_template_decl;
+  if (result != error_mark_node)
+    {
+      tree decl = DECL_TEMPLATE_RESULT (tmpl);
+      return build_tree_list (result, decl);
+    }
+  else
+    return error_mark_node;
+}
+
+
+/* Given a call expression or template-id expression to
+  a concept EXPR possibly including a wildcard, deduce
+  the concept being checked and the prototype parameter.
+  Returns true if the constraint and prototype can be
+  deduced and false otherwise.  Note that the CHECK and
+  PROTO arguments are set to NULL_TREE if this returns
+  false.  */
+
+bool
+deduce_constrained_parameter (tree expr, tree& check, tree& proto)
+{
+  tree info = NULL_TREE;
+  if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
+    info = resolve_variable_concept_check (expr);
+  else if (TREE_CODE (expr) == CALL_EXPR)
+    info = resolve_constraint_check (expr);
+  else
+    gcc_unreachable ();
+
+  if (info && info != error_mark_node)
+    {
+      check = TREE_VALUE (info);
+      tree arg = TREE_VEC_ELT (TREE_PURPOSE (info), 0);
+      if (ARGUMENT_PACK_P (arg))
+	arg = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0);
+      proto = TREE_TYPE (arg);
+      return true;
+    }
+  check = proto = NULL_TREE;
+  return false;
+}
+
+// Given a call expression or template-id expression to a concept, EXPR,
+// deduce the concept being checked and return the template arguments.
+// Returns NULL_TREE if deduction fails.
+static tree
+deduce_concept_introduction (tree expr)
+{
+  tree info = NULL_TREE;
+  if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
+    info = resolve_variable_concept_check (expr);
+  else if (TREE_CODE (expr) == CALL_EXPR)
+    info = resolve_constraint_check (expr);
+  else
+    gcc_unreachable ();
+
+  if (info && info != error_mark_node)
+    return TREE_PURPOSE (info);
+  return NULL_TREE;
+}
+
+namespace {
+
+/*---------------------------------------------------------------------------
+                       Constraint implication learning
+---------------------------------------------------------------------------*/
+
+/* The implication context determines how we memoize concept checks.
+   Given two checks C1 and C2, the direction of implication depends
+   on whether we are learning implications of a conjunction or disjunction.
+   For example:
+
+      template<typename T> concept bool C = ...;
+      template<typenaem T> concept bool D = C<T> && true;
+
+   From this, we can learn that D<T> implies C<T>. We cannot learn,
+   without further testing, that C<T> does not imply D<T>. If, for
+   example, C<T> were defined as true, then these constraints would
+   be logically equivalent.
+
+   In rare cases, we may start with a logical equivalence. For example:
+
+      template<typename T> concept bool C = ...;
+      template<typename T> concept bool D = C<T>;
+
+   Here, we learn that C<T> implies D<T> and vice versa.   */
+
+enum implication_context
+{
+  conjunction_cxt, /* C1 implies C2. */
+  disjunction_cxt, /* C2 implies C1. */
+  equivalence_cxt  /* C1 implies C2, C2 implies C1. */
+};
+
+void learn_implications(tree, tree, implication_context);
+
+void
+learn_implication (tree parent, tree child, implication_context cxt)
+{
+  switch (cxt)
+    {
+      case conjunction_cxt:
+        save_subsumption_result (parent, child, true);
+        break;
+      case disjunction_cxt:
+        save_subsumption_result (child, parent, true);
+        break;
+      case equivalence_cxt:
+        save_subsumption_result (parent, child, true);
+        save_subsumption_result (child, parent, true);
+        break;
+    }
+}
+
+void
+learn_logical_operation (tree parent, tree constr, implication_context cxt)
+{
+  learn_implications (parent, TREE_OPERAND (constr, 0), cxt);
+  learn_implications (parent, TREE_OPERAND (constr, 1), cxt);
+}
+
+void
+learn_implications (tree parent, tree constr, implication_context cxt)
+{
+  switch (TREE_CODE (constr))
+    {
+      case CHECK_CONSTR:
+        return learn_implication (parent, constr, cxt);
+
+      case CONJ_CONSTR:
+        if (cxt == disjunction_cxt)
+          return;
+        return learn_logical_operation (parent, constr, cxt);
+
+      case DISJ_CONSTR:
+        if (cxt == conjunction_cxt)
+          return;
+        return learn_logical_operation (parent, constr, cxt);
+
+      default:
+        break;
+    }
+}
+
+/* Quickly scan the top-level constraints of CONSTR to learn and
+   cache logical relations between concepts.  The search does not
+   include conjunctions of disjunctions or vice versa.  */
+
+void
+learn_implications (tree tmpl, tree args, tree constr)
+{
+  /* Don't memoize relations between non-dependent arguemnts. It's not
+     helpful. */
+  if (!uses_template_parms (args))
+    return;
+
+  /* Build a check constraint for the purpose of caching. */
+  tree parent = build_nt (CHECK_CONSTR, tmpl, args);
+
+  /* Start learning based on the kind of the top-level contraint. */
+  if (TREE_CODE (constr) == CONJ_CONSTR)
+    return learn_logical_operation (parent, constr, conjunction_cxt);
+  else if (TREE_CODE (constr) == DISJ_CONSTR)
+    return learn_logical_operation (parent, constr, disjunction_cxt);
+  else if (TREE_CODE (constr) == CHECK_CONSTR)
+    /* This is the rare concept alias case. */
+    return learn_implication (parent, constr, equivalence_cxt);
+}
+
+/*---------------------------------------------------------------------------
+                       Expansion of concept definitions
+---------------------------------------------------------------------------*/
+
+/* Returns the expression of a function concept. */
+
+tree
+get_returned_expression (tree fn)
+{
+  /* Extract the body of the function minus the return expression.  */
+  tree body = DECL_SAVED_TREE (fn);
+  if (!body)
+    return error_mark_node;
+  if (TREE_CODE (body) == BIND_EXPR)
+    body = BIND_EXPR_BODY (body);
+  if (TREE_CODE (body) != RETURN_EXPR)
+    return error_mark_node;
+
+  return TREE_OPERAND (body, 0);
+}
+
+/* Returns the initializer of a variable concept. */
+
+tree
+get_variable_initializer (tree var)
+{
+  tree init = DECL_INITIAL (var);
+  if (!init)
+    return error_mark_node;
+  return init;
+}
+
+/* Returns the definition of a variable or function concept.  */
+
+tree
+get_concept_definition (tree decl)
+{
+  if (VAR_P (decl))
+    return get_variable_initializer (decl);
+  else if (TREE_CODE (decl) == FUNCTION_DECL)
+    return get_returned_expression (decl);
+  gcc_unreachable ();
+}
+
+int expansion_level = 0;
+
+struct expanding_concept_sentinel
+{
+  expanding_concept_sentinel ()
+  {
+    ++expansion_level;
+  }
+
+  ~expanding_concept_sentinel()
+  {
+    --expansion_level;
+  }
+};
+
+
+} /* namespace */
+
+/* Returns true when a concept is being expanded.  */
+
+bool
+expanding_concept()
+{
+  return expansion_level > 0;
+}
+
+/* Expand a concept declaration (not a template) and its arguments to
+   a constraint defined by the concept's initializer or definition.  */
+
+tree
+expand_concept (tree decl, tree args)
+{
+  expanding_concept_sentinel sentinel;
+
+  if (TREE_CODE (decl) == TEMPLATE_DECL)
+    decl = DECL_TEMPLATE_RESULT (decl);
+  tree tmpl = DECL_TI_TEMPLATE (decl);
+
+  /* Check for a previous specialization. */
+  if (tree spec = get_concept_expansion (tmpl, args))
+    return spec;
+
+  /* Substitute the arguments to form a new definition expression.  */
+  tree def = get_concept_definition (decl);
+
+  ++processing_template_decl;
+  tree result = tsubst_expr (def, args, tf_none, NULL_TREE, true);
+  --processing_template_decl;
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  /* And lastly, normalize it, check for implications, and save
+     the specialization for later.  */
+  tree norm = normalize_expression (result);
+  learn_implications (tmpl, args, norm);
+  return save_concept_expansion (tmpl, args, norm);
+}
+
+
+/*---------------------------------------------------------------------------
+                Stepwise normalization of expressions
+
+This set of functions will transform an expression into a constraint
+in a sequence of steps. Normalization does not not look into concept
+definitions.
+---------------------------------------------------------------------------*/
+
+/* Transform a logical-or or logical-and expression into either
+   a conjunction or disjunction. */
+
+tree
+normalize_logical_operation (tree t, tree_code c)
+{
+  tree t0 = normalize_expression (TREE_OPERAND (t, 0));
+  tree t1 = normalize_expression (TREE_OPERAND (t, 1));
+  return build_nt (c, t0, t1);
+}
+
+/* A simple requirement T introduces an expression constraint
+   for its expression. */
+
+inline tree
+normalize_simple_requirement (tree t)
+{
+  return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0));
+}
+
+/* A type requirement T introduce a type constraint for its type.  */
+
+inline tree
+normalize_type_requirement (tree t)
+{
+  return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0));
+}
+
+/* A compound requirement T introduces a conjunction of constraints
+   depending on its form.  The conjunction always includes an
+   expression constraint for the expression of the requirement.
+   If a trailing return type was specified, the conjunction includes
+   either an implicit conversion constraint or an argument deduction
+   constraint.  If the noexcept specifier is present, the conjunction
+   includes an exception constraint.  */
+
+tree
+normalize_compound_requirement (tree t)
+{
+  tree expr = TREE_OPERAND (t, 0);
+  tree constr = build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0));
+
+  /* If a type is given, append an implicit conversion or
+     argument deduction constraint.  */
+  if (tree type = TREE_OPERAND (t, 1))
+    {
+      tree type_constr;
+      /* TODO: We should be extracting a list of auto nodes
+         from type_uses_auto, not a single node */
+      if (tree placeholder = type_uses_auto (type))
+        type_constr = build_nt (DEDUCT_CONSTR, expr, type, placeholder);
+      else
+        type_constr = build_nt (ICONV_CONSTR, expr, type);
+      constr = conjoin_constraints (constr, type_constr);
+    }
+
+  /* If noexcept is present, append an exception constraint. */
+  if (COMPOUND_REQ_NOEXCEPT_P (t))
+    {
+      tree except = build_nt (EXCEPT_CONSTR, expr);
+      constr = conjoin_constraints (constr, except);
+    }
+
+  return constr;
+}
+
+/* A nested requirement T introduces a conjunction of constraints
+   corresponding to its constraint-expression.
+
+   If the result of transforming T is error_mark_node, the resulting
+   constraint is a predicate constraint whose operand is also
+   error_mark_node. This preserves the constraint structure, but
+   will guarantee that the constraint is never satisfied.  */
+
+inline tree
+normalize_nested_requirement (tree t)
+{
+  return normalize_expression (TREE_OPERAND (t, 0));
+}
+
+/* Transform a requirement T into one or more constraints.  */
+
+tree
+normalize_requirement (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case SIMPLE_REQ:
+      return normalize_simple_requirement (t);
+
+    case TYPE_REQ:
+      return normalize_type_requirement (t);
+
+    case COMPOUND_REQ:
+      return normalize_compound_requirement (t);
+
+    case NESTED_REQ:
+      return normalize_nested_requirement (t);
+
+    default:
+      gcc_unreachable ();
+    }
+  return error_mark_node;
+}
+
+/* Transform a sequence of requirements into a conjunction of
+   constraints. */
+
+tree
+normalize_requirements (tree t)
+{
+  tree result = NULL_TREE;
+  for (; t; t = TREE_CHAIN (t))
+    {
+      tree constr = normalize_requirement (TREE_VALUE (t));
+      result = conjoin_constraints (result, constr);
+    }
+  return result;
+}
+
+/* The normal form of a requires-expression is a parameterized
+   constraint having the same parameters and a conjunction of
+   constraints representing the normal form of requirements.  */
+
+tree
+normalize_requires_expression (tree t)
+{
+  tree operand = normalize_requirements (TREE_OPERAND (t, 1));
+  if (tree parms = TREE_OPERAND (t, 0))
+    return build_nt (PARM_CONSTR, parms, operand);
+  else
+    return operand;
+}
+
+/* For a template-id referring to a variable concept, returns
+   a check constraint. Otherwise, returns a predicate constraint. */
+
+tree
+normalize_template_id_expression (tree t)
+{
+  if (tree info = resolve_variable_concept_check (t))
+    {
+      if (info == error_mark_node)
+        {
+          /* We get this when the template arguments don't match
+             the variable concept. */
+          error ("invalid reference to concept %qE", t);
+          return error_mark_node;
+        }
+
+      tree decl = TREE_VALUE (info);
+      tree args = TREE_PURPOSE (info);
+      return build_nt (CHECK_CONSTR, decl, args);
+    }
+
+  /* Check that we didn't refer to a function concept like a variable.  */
+  tree fn = OVL_FIRST (TREE_OPERAND (t, 0));
+  if (TREE_CODE (fn) == TEMPLATE_DECL
+      && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn)))
+    {
+      error_at (location_of (t),
+		"invalid reference to function concept %qD", fn);
+      return error_mark_node;
+    }
+
+  return build_nt (PRED_CONSTR, t);
+}
+
+/* For a call expression to a function concept, returns a check
+   constraint. Otherwise, returns a predicate constraint. */
+
+tree
+normalize_call_expression (tree t)
+{
+  /* Try to resolve this function call as a concept.  If not, then
+     it can be returned as a predicate constraint.  */
+  tree check = resolve_constraint_check (t);
+  if (!check)
+    return build_nt (PRED_CONSTR, t);
+  if (check == error_mark_node)
+    {
+      /* TODO: Improve diagnostics. We could report why the reference
+         is invalid. */
+      error ("invalid reference to concept %qE", t);
+      return error_mark_node;
+    }
+
+  tree fn = TREE_VALUE (check);
+  tree args = TREE_PURPOSE (check);
+  return build_nt (CHECK_CONSTR, fn, args);
+}
+
+/* If T is a call to an overloaded && or || operator, diagnose that
+   as a non-SFINAEable error.  Returns true if an error is emitted.
+
+   TODO: It would be better to diagnose this at the point of definition,
+   if possible. Perhaps we should immediately do a first-pass normalization
+   of a concept definition to catch obvious non-dependent errors like
+   this.  */
+
+bool
+check_for_logical_overloads (tree t)
+{
+  if (TREE_CODE (t) != CALL_EXPR)
+    return false;
+
+  tree fn = CALL_EXPR_FN (t);
+
+  /* For member calls, try extracting the function from the
+     component ref.  */
+  if (TREE_CODE (fn) == COMPONENT_REF)
+    {
+      fn = TREE_OPERAND (fn, 1);
+      if (TREE_CODE (fn) == BASELINK)
+        fn = BASELINK_FUNCTIONS (fn);
+    }
+
+  if (TREE_CODE (fn) != FUNCTION_DECL)
+    return false;
+
+  if (DECL_OVERLOADED_OPERATOR_P (fn))
+    {
+      location_t loc = EXPR_LOC_OR_LOC (t, input_location);
+      error_at (loc, "constraint %qE, uses overloaded operator", t);
+      return true;
+    }
+
+  return false;
+}
+
+/* The normal form of an atom depends on the expression. The normal
+   form of a function call to a function concept is a check constraint
+   for that concept. The normal form of a reference to a variable
+   concept is a check constraint for that concept. Otherwise, the
+   constraint is a predicate constraint.  */
+
+tree
+normalize_atom (tree t)
+{
+  /* We can get constraints pushed down through pack expansions, so
+     just return them. */
+  if (constraint_p (t))
+    return t;
+
+  tree type = TREE_TYPE (t);
+  if (!type || type_unknown_p (t) || TREE_CODE (type) == TEMPLATE_TYPE_PARM)
+    ;
+  else if (!dependent_type_p (type))
+    {
+      if (check_for_logical_overloads (t))
+        return error_mark_node;
+
+      type = cv_unqualified (type);
+      if (!same_type_p (type, boolean_type_node))
+	{
+	  error ("predicate constraint %q+E does not have type %<bool%>", t);
+	  return error_mark_node;
+	}
+    }
+
+  if (TREE_CODE (t) == TEMPLATE_ID_EXPR)
+    return normalize_template_id_expression (t);
+  if (TREE_CODE (t) == CALL_EXPR)
+    return normalize_call_expression (t);
+  return build_nt (PRED_CONSTR, t);
+}
+
+/* Push down the pack expansion EXP into the leaves of the constraint PAT.  */
+
+tree
+push_down_pack_expansion (tree exp, tree pat)
+{
+  switch (TREE_CODE (pat))
+    {
+    case CONJ_CONSTR:
+    case DISJ_CONSTR:
+      {
+	pat = copy_node (pat);
+	TREE_OPERAND (pat, 0)
+	  = push_down_pack_expansion (exp, TREE_OPERAND (pat, 0));
+	TREE_OPERAND (pat, 1)
+	  = push_down_pack_expansion (exp, TREE_OPERAND (pat, 1));
+	return pat;
+      }
+    default:
+      {
+	exp = copy_node (exp);
+	SET_PACK_EXPANSION_PATTERN (exp, pat);
+	return exp;
+      }
+    }
+}
+
+/* Transform a pack expansion into a constraint.  First we transform the
+   pattern of the pack expansion, then we push the pack expansion down into the
+   leaves of the constraint so that partial ordering will work.  */
+
+tree
+normalize_pack_expansion (tree t)
+{
+  tree pat = normalize_expression (PACK_EXPANSION_PATTERN (t));
+  return push_down_pack_expansion (t, pat);
+}
+
+/* Transform an expression into a constraint.  */
+
+tree
+normalize_any_expression (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case TRUTH_ANDIF_EXPR:
+      return normalize_logical_operation (t, CONJ_CONSTR);
+
+    case TRUTH_ORIF_EXPR:
+      return normalize_logical_operation (t, DISJ_CONSTR);
+
+    case REQUIRES_EXPR:
+      return normalize_requires_expression (t);
+
+    case BIND_EXPR:
+      return normalize_expression (BIND_EXPR_BODY (t));
+
+    case EXPR_PACK_EXPANSION:
+      return normalize_pack_expansion (t);
+
+    default:
+      /* All other constraints are atomic. */
+      return normalize_atom (t);
+    }
+}
+
+/* Transform a statement into an expression.  */
+tree
+normalize_any_statement (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case RETURN_EXPR:
+      return normalize_expression (TREE_OPERAND (t, 0));
+    default:
+      gcc_unreachable ();
+    }
+  return error_mark_node;
+}
+
+/* Reduction rules for the declaration T.  */
+
+tree
+normalize_any_declaration (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+      return normalize_atom (t);
+    default:
+      gcc_unreachable ();
+    }
+  return error_mark_node;
+}
+
+/* Returns the normal form of a constraint expression. */
+
+tree
+normalize_expression (tree t)
+{
+  if (!t)
+    return NULL_TREE;
+
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  switch (TREE_CODE_CLASS (TREE_CODE (t)))
+    {
+    case tcc_unary:
+    case tcc_binary:
+    case tcc_expression:
+    case tcc_vl_exp:
+      return normalize_any_expression (t);
+
+    case tcc_statement:
+      return normalize_any_statement (t);
+
+    case tcc_declaration:
+      return normalize_any_declaration (t);
+
+    case tcc_exceptional:
+    case tcc_constant:
+    case tcc_reference:
+    case tcc_comparison:
+      /* These are all atomic predicate constraints. */
+      return normalize_atom (t);
+
+    default:
+      /* Unhandled node kind. */
+      gcc_unreachable ();
+    }
+  return error_mark_node;
+}
+
+
+/*---------------------------------------------------------------------------
+                        Constraint normalization
+---------------------------------------------------------------------------*/
+
+tree normalize_constraint (tree);
+
+/* The normal form of the disjunction T0 /\ T1 is the conjunction
+   of the normal form of T0 and the normal form of T1.  */
+
+inline tree
+normalize_conjunction (tree t)
+{
+  tree t0 = normalize_constraint (TREE_OPERAND (t, 0));
+  tree t1 = normalize_constraint (TREE_OPERAND (t, 1));
+  return build_nt (CONJ_CONSTR, t0, t1);
+}
+
+/* The normal form of the disjunction T0 \/ T1 is the disjunction
+   of the normal form of T0 and the normal form of T1.  */
+
+inline tree
+normalize_disjunction (tree t)
+{
+  tree t0 = normalize_constraint (TREE_OPERAND (t, 0));
+  tree t1 = normalize_constraint (TREE_OPERAND (t, 1));
+  return build_nt (DISJ_CONSTR, t0, t1);
+}
+
+/* A predicate constraint is normalized in two stages.  First all
+   references specializations of concepts are replaced by their
+   substituted definitions.  Then, the resulting expression is
+   transformed into a constraint by transforming && expressions
+   into conjunctions and || into disjunctions.  */
+
+tree
+normalize_predicate_constraint (tree t)
+{
+  ++processing_template_decl;
+  tree expr = PRED_CONSTR_EXPR (t);
+  tree constr = normalize_expression (expr);
+  --processing_template_decl;
+  return constr;
+}
+
+/* The normal form of a parameterized constraint is the normal
+   form of its operand.  */
+
+tree
+normalize_parameterized_constraint (tree t)
+{
+  tree parms = PARM_CONSTR_PARMS (t);
+  tree operand = normalize_constraint (PARM_CONSTR_OPERAND (t));
+  return build_nt (PARM_CONSTR, parms, operand);
+}
+
+/* Normalize the constraint T by reducing it so that it is
+   comprised of only conjunctions and disjunctions of atomic
+   constraints.  */
+
+tree
+normalize_constraint (tree t)
+{
+  if (!t)
+    return NULL_TREE;
+
+  if (t == error_mark_node)
+    return t;
+
+  switch (TREE_CODE (t))
+    {
+      case CONJ_CONSTR:
+        return normalize_conjunction (t);
+
+      case DISJ_CONSTR:
+        return normalize_disjunction (t);
+
+      case PRED_CONSTR:
+        return normalize_predicate_constraint (t);
+
+      case PARM_CONSTR:
+        return normalize_parameterized_constraint (t);
+
+      case EXPR_CONSTR:
+      case TYPE_CONSTR:
+      case ICONV_CONSTR:
+      case DEDUCT_CONSTR:
+      case EXCEPT_CONSTR:
+        /* These constraints are defined to be atomic. */
+        return t;
+
+      default:
+        /* CONSTR was not a constraint. */
+        gcc_unreachable();
+    }
+  return error_mark_node;
+}
+
+
+
+// -------------------------------------------------------------------------- //
+// Constraint Semantic Processing
+//
+// The following functions are called by the parser and substitution rules
+// to create and evaluate constraint-related nodes.
+
+// The constraints associated with the current template parameters.
+tree
+current_template_constraints (void)
+{
+  if (!current_template_parms)
+    return NULL_TREE;
+  tree tmpl_constr = TEMPLATE_PARM_CONSTRAINTS (current_template_parms);
+  return build_constraints (tmpl_constr, NULL_TREE);
+}
+
+// If the recently parsed TYPE declares or defines a template or template
+// specialization, get its corresponding constraints from the current
+// template parameters and bind them to TYPE's declaration.
+tree
+associate_classtype_constraints (tree type)
+{
+  if (!type || type == error_mark_node || TREE_CODE (type) != RECORD_TYPE)
+    return type;
+
+  // An explicit class template specialization has no template
+  // parameters.
+  if (!current_template_parms)
+    return type;
+
+  if (CLASSTYPE_IS_TEMPLATE (type) || CLASSTYPE_TEMPLATE_SPECIALIZATION (type))
+    {
+      tree decl = TYPE_STUB_DECL (type);
+      tree ci = current_template_constraints ();
+
+      // An implicitly instantiated member template declaration already
+      // has associated constraints. If it is defined outside of its
+      // class, then we need match these constraints against those of
+      // original declaration.
+      if (tree orig_ci = get_constraints (decl))
+        {
+          if (!equivalent_constraints (ci, orig_ci))
+            {
+              // FIXME: Improve diagnostics.
+              error ("%qT does not match any declaration", type);
+              return error_mark_node;
+            }
+          return type;
+        }
+      set_constraints (decl, ci);
+    }
+  return type;
+}
+
+namespace {
+
+// Create an empty constraint info block.
+inline tree_constraint_info*
+build_constraint_info ()
+{
+  return (tree_constraint_info *)make_node (CONSTRAINT_INFO);
+}
+
+} // namespace
+
+/* Build a constraint-info object that contains the associated constraints
+   of a declaration.  This also includes the declaration's template
+   requirements (TREQS) and any trailing requirements for a function
+   declarator (DREQS).  Note that both TREQS and DREQS must be constraints.
+
+   If the declaration has neither template nor declaration requirements
+   this returns NULL_TREE, indicating an unconstrained declaration.  */
+
+tree
+build_constraints (tree tmpl_reqs, tree decl_reqs)
+{
+  gcc_assert (tmpl_reqs ? constraint_p (tmpl_reqs) : true);
+  gcc_assert (decl_reqs ? constraint_p (decl_reqs) : true);
+
+  if (!tmpl_reqs && !decl_reqs)
+    return NULL_TREE;
+
+  tree_constraint_info* ci = build_constraint_info ();
+  ci->template_reqs = tmpl_reqs;
+  ci->declarator_reqs = decl_reqs;
+  ci->associated_constr = conjoin_constraints (tmpl_reqs, decl_reqs);
+
+  return (tree)ci;
+}
+
+namespace {
+
+/* Construct a sequence of template arguments by prepending
+   ARG to REST. Either ARG or REST may be null. */
+tree
+build_concept_check_arguments (tree arg, tree rest)
+{
+  gcc_assert (rest ? TREE_CODE (rest) == TREE_VEC : true);
+  tree args;
+  if (arg)
+    {
+      int n = rest ? TREE_VEC_LENGTH (rest) : 0;
+      args = make_tree_vec (n + 1);
+      TREE_VEC_ELT (args, 0) = arg;
+      if (rest)
+        for (int i = 0; i < n; ++i)
+          TREE_VEC_ELT (args, i + 1) = TREE_VEC_ELT (rest, i);
+      int def = rest ? GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (rest) : 0;
+      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, def + 1);
+    }
+  else
+    {
+      gcc_assert (rest != NULL_TREE);
+      args = rest;
+    }
+  return args;
+}
+
+} // namespace
+
+/* Construct an expression that checks the concept given by
+   TARGET. The TARGET must be:
+
+   - an OVERLOAD referring to one or more function concepts
+   - a BASELINK referring to an overload set of the above, or
+   - a TEMPLTATE_DECL referring to a variable concept.
+
+   ARG and REST are the explicit template arguments for the
+   eventual concept check. */
+tree
+build_concept_check (tree target, tree arg, tree rest)
+{
+  tree args = build_concept_check_arguments (arg, rest);
+  if (variable_template_p (target))
+    return build_variable_check (lookup_template_variable (target, args));
+  else
+    return build_call_check (lookup_template_function (target, args));
+}
+
+
+/* Returns a TYPE_DECL that contains sufficient information to
+   build a template parameter of the same kind as PROTO and
+   constrained by the concept declaration CNC.  Note that PROTO
+   is the first template parameter of CNC.
+
+   If specified, ARGS provides additional arguments to the
+   constraint check.  */
+tree
+build_constrained_parameter (tree cnc, tree proto, tree args)
+{
+  tree name = DECL_NAME (cnc);
+  tree type = TREE_TYPE (proto);
+  tree decl = build_decl (input_location, TYPE_DECL, name, type);
+  CONSTRAINED_PARM_PROTOTYPE (decl) = proto;
+  CONSTRAINED_PARM_CONCEPT (decl) = cnc;
+  CONSTRAINED_PARM_EXTRA_ARGS (decl) = args;
+  return decl;
+}
+
+/* Create a constraint expression for the given DECL that
+   evaluates the requirements specified by CONSTR, a TYPE_DECL
+   that contains all the information necessary to build the
+   requirements (see finish_concept_name for the layout of
+   that TYPE_DECL).
+
+   Note that the constraints are neither reduced nor decomposed.
+   That is done only after the requires clause has been parsed
+   (or not).
+
+   This will always return a CHECK_CONSTR. */
+tree
+finish_shorthand_constraint (tree decl, tree constr)
+{
+  /* No requirements means no constraints.  */
+  if (!constr)
+    return NULL_TREE;
+
+  tree proto = CONSTRAINED_PARM_PROTOTYPE (constr);
+  tree con = CONSTRAINED_PARM_CONCEPT (constr);
+  tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
+
+  /* If the parameter declaration is variadic, but the concept
+     is not then we need to apply the concept to every element
+     in the pack.  */
+  bool is_proto_pack = template_parameter_pack_p (proto);
+  bool is_decl_pack = template_parameter_pack_p (decl);
+  bool apply_to_all_p = is_decl_pack && !is_proto_pack;
+
+  /* Get the argument and overload used for the requirement
+     and adjust it if we're going to expand later.  */
+  tree arg = template_parm_to_arg (build_tree_list (NULL_TREE, decl));
+  if (apply_to_all_p)
+    arg = PACK_EXPANSION_PATTERN (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0));
+
+  /* Build the concept check. If it the constraint needs to be
+     applied to all elements of the parameter pack, then make
+     the constraint an expansion. */
+  tree tmpl = DECL_TI_TEMPLATE (con);
+  tree check = VAR_P (con) ? tmpl : ovl_make (tmpl);
+  check = build_concept_check (check, arg, args);
+
+  /* Make the check a pack expansion if needed.
+
+     FIXME: We should be making a fold expression. */
+  if (apply_to_all_p)
+    {
+      check = make_pack_expansion (check);
+      TREE_TYPE (check) = boolean_type_node;
+    }
+
+  return normalize_expression (check);
+}
+
+/* Returns a conjunction of shorthand requirements for the template
+   parameter list PARMS. Note that the requirements are stored in
+   the TYPE of each tree node. */
+tree
+get_shorthand_constraints (tree parms)
+{
+  tree result = NULL_TREE;
+  parms = INNERMOST_TEMPLATE_PARMS (parms);
+  for (int i = 0; i < TREE_VEC_LENGTH (parms); ++i)
+    {
+      tree parm = TREE_VEC_ELT (parms, i);
+      tree constr = TEMPLATE_PARM_CONSTRAINTS (parm);
+      result = conjoin_constraints (result, constr);
+    }
+  return result;
+}
+
+// Returns and chains a new parameter for PARAMETER_LIST which will conform
+// to the prototype given by SRC_PARM.  The new parameter will have its
+// identifier and location set according to IDENT and PARM_LOC respectively.
+static tree
+process_introduction_parm (tree parameter_list, tree src_parm)
+{
+  // If we have a pack, we should have a single pack argument which is the
+  // placeholder we want to look at.
+  bool is_parameter_pack = ARGUMENT_PACK_P (src_parm);
+  if (is_parameter_pack)
+    src_parm = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (src_parm), 0);
+
+  // At this point we should have a wildcard, but we want to
+  // grab the associated decl from it.  Also grab the stored
+  // identifier and location that should be chained to it in
+  // a PARM_DECL.
+  gcc_assert (TREE_CODE (src_parm) == WILDCARD_DECL);
+
+  tree ident = DECL_NAME (src_parm);
+  location_t parm_loc = DECL_SOURCE_LOCATION (src_parm);
+
+  // If we expect a pack and the deduced template is not a pack, or if the
+  // template is using a pack and we didn't declare a pack, throw an error.
+  if (is_parameter_pack != WILDCARD_PACK_P (src_parm))
+    {
+      error_at (parm_loc, "cannot match pack for introduced parameter");
+      tree err_parm = build_tree_list (error_mark_node, error_mark_node);
+      return chainon (parameter_list, err_parm);
+    }
+
+  src_parm = TREE_TYPE (src_parm);
+
+  tree parm;
+  bool is_non_type;
+  if (TREE_CODE (src_parm) == TYPE_DECL)
+    {
+      is_non_type = false;
+      parm = finish_template_type_parm (class_type_node, ident);
+    }
+  else if (TREE_CODE (src_parm) == TEMPLATE_DECL)
+    {
+      is_non_type = false;
+      begin_template_parm_list ();
+      current_template_parms = DECL_TEMPLATE_PARMS (src_parm);
+      end_template_parm_list ();
+      parm = finish_template_template_parm (class_type_node, ident);
+    }
+  else
+    {
+      is_non_type = true;
+
+      // Since we don't have a declarator, so we can copy the source
+      // parameter and change the name and eventually the location.
+      parm = copy_decl (src_parm);
+      DECL_NAME (parm) = ident;
+    }
+
+  // Wrap in a TREE_LIST for process_template_parm.  Introductions do not
+  // retain the defaults from the source template.
+  parm = build_tree_list (NULL_TREE, parm);
+
+  return process_template_parm (parameter_list, parm_loc, parm,
+                                is_non_type, is_parameter_pack);
+}
+
+/* Associates a constraint check to the current template based
+   on the introduction parameters.  INTRO_LIST must be a TREE_VEC
+   of WILDCARD_DECLs containing a chained PARM_DECL which
+   contains the identifier as well as the source location.
+   TMPL_DECL is the decl for the concept being used.  If we
+   take a concept, C, this will form a check in the form of
+   C<INTRO_LIST> filling in any extra arguments needed by the
+   defaults deduced.
+
+   Returns NULL_TREE if no concept could be matched and
+   error_mark_node if an error occurred when matching.  */
+tree
+finish_template_introduction (tree tmpl_decl, tree intro_list)
+{
+  /* Deduce the concept check.  */
+  tree expr = build_concept_check (tmpl_decl, NULL_TREE, intro_list);
+  if (expr == error_mark_node)
+    return NULL_TREE;
+
+  tree parms = deduce_concept_introduction (expr);
+  if (!parms)
+    return NULL_TREE;
+
+  /* Build template parameter scope for introduction.  */
+  tree parm_list = NULL_TREE;
+  begin_template_parm_list ();
+  int nargs = MIN (TREE_VEC_LENGTH (parms), TREE_VEC_LENGTH (intro_list));
+  for (int n = 0; n < nargs; ++n)
+    parm_list = process_introduction_parm (parm_list, TREE_VEC_ELT (parms, n));
+  parm_list = end_template_parm_list (parm_list);
+  for (int i = 0; i < TREE_VEC_LENGTH (parm_list); ++i)
+    if (TREE_VALUE (TREE_VEC_ELT (parm_list, i)) == error_mark_node)
+      {
+        end_template_decl ();
+        return error_mark_node;
+      }
+
+  /* Build a concept check for our constraint.  */
+  tree check_args = make_tree_vec (TREE_VEC_LENGTH (parms));
+  int n = 0;
+  for (; n < TREE_VEC_LENGTH (parm_list); ++n)
+    {
+      tree parm = TREE_VEC_ELT (parm_list, n);
+      TREE_VEC_ELT (check_args, n) = template_parm_to_arg (parm);
+    }
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (check_args, n);
+
+  /* If the template expects more parameters we should be able
+     to use the defaults from our deduced concept.  */
+  for (; n < TREE_VEC_LENGTH (parms); ++n)
+    TREE_VEC_ELT (check_args, n) = TREE_VEC_ELT (parms, n);
+
+  /* Associate the constraint. */
+  tree check = build_concept_check (tmpl_decl, NULL_TREE, check_args);
+  tree constr = normalize_expression (check);
+  TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = constr;
+
+  return parm_list;
+}
+
+
+/* Given the predicate constraint T from a constrained-type-specifier, extract
+   its TMPL and ARGS.  FIXME why do we need two different forms of
+   constrained-type-specifier?  */
+
+void
+placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args)
+{
+  if (TREE_CODE (t) == TYPE_DECL)
+    {
+      /* A constrained parameter.  Build a constraint check
+         based on the prototype parameter and then extract the
+         arguments from that.  */
+      tree proto = CONSTRAINED_PARM_PROTOTYPE (t);
+      tree check = finish_shorthand_constraint (proto, t);
+      placeholder_extract_concept_and_args (check, tmpl, args);
+      return;
+    }
+
+  if (TREE_CODE (t) == CHECK_CONSTR)
+    {
+      tree decl = CHECK_CONSTR_CONCEPT (t);
+      tmpl = DECL_TI_TEMPLATE (decl);
+      args = CHECK_CONSTR_ARGS (t);
+      return;
+    }
+
+    gcc_unreachable ();
+}
+
+/* Returns true iff the placeholders C1 and C2 are equivalent.  C1
+   and C2 can be either CHECK_CONSTR or TEMPLATE_TYPE_PARM.  */
+
+bool
+equivalent_placeholder_constraints (tree c1, tree c2)
+{
+  if (c1 && TREE_CODE (c1) == TEMPLATE_TYPE_PARM)
+    /* A constrained auto.  */
+    c1 = PLACEHOLDER_TYPE_CONSTRAINTS (c1);
+  if (c2 && TREE_CODE (c2) == TEMPLATE_TYPE_PARM)
+    c2 = PLACEHOLDER_TYPE_CONSTRAINTS (c2);
+
+  if (c1 == c2)
+    return true;
+  if (!c1 || !c2)
+    return false;
+  if (c1 == error_mark_node || c2 == error_mark_node)
+    /* We get here during satisfaction; when a deduction constraint
+       fails, substitution can produce an error_mark_node for the
+       placeholder constraints.  */
+    return false;
+
+  tree t1, t2, a1, a2;
+  placeholder_extract_concept_and_args (c1, t1, a1);
+  placeholder_extract_concept_and_args (c2, t2, a2);
+
+  if (t1 != t2)
+    return false;
+
+  int len1 = TREE_VEC_LENGTH (a1);
+  int len2 = TREE_VEC_LENGTH (a2);
+  if (len1 != len2)
+    return false;
+
+  /* Skip the first argument so we don't infinitely recurse.
+     Also, they may differ in template parameter index.  */
+  for (int i = 1; i < len1; ++i)
+    {
+      tree t1 = TREE_VEC_ELT (a1, i);
+      tree t2 = TREE_VEC_ELT (a2, i);
+      if (!template_args_equal (t1, t2))
+      return false;
+    }
+  return true;
+}
+
+/* Return a hash value for the placeholder PRED_CONSTR C.  */
+
+hashval_t
+hash_placeholder_constraint (tree c)
+{
+  tree t, a;
+  placeholder_extract_concept_and_args (c, t, a);
+
+  /* Like hash_tmpl_and_args, but skip the first argument.  */
+  hashval_t val = iterative_hash_object (DECL_UID (t), 0);
+
+  for (int i = TREE_VEC_LENGTH (a)-1; i > 0; --i)
+    val = iterative_hash_template_arg (TREE_VEC_ELT (a, i), val);
+
+  return val;
+}
+
+/*---------------------------------------------------------------------------
+                        Constraint substitution
+---------------------------------------------------------------------------*/
+
+/* The following functions implement substitution rules for constraints.
+   Substitution without checking constraints happens only in the
+   instantiation of class templates. For example:
+
+      template<C1 T> struct S {
+        void f(T) requires C2<T>;
+        void g(T) requires T::value;
+      };
+
+      S<int> s; // error instantiating S<int>::g(T)
+
+   When we instantiate S, we substitute into its member declarations,
+   including their constraints. However, those constraints are not
+   checked. Substituting int into C2<T> yields C2<int>, and substituting
+   into T::value yields a substitution failure, making the program
+   ill-formed.
+
+   Note that we only ever substitute into the associated constraints
+   of a declaration. That is, substitution is defined only for predicate
+   constraints and conjunctions. */
+
+/* Substitute into the predicate constraints. Returns error_mark_node
+   if the substitution into the expression fails. */
+tree
+tsubst_predicate_constraint (tree t, tree args,
+                             tsubst_flags_t complain, tree in_decl)
+{
+  tree expr = PRED_CONSTR_EXPR (t);
+  ++processing_template_decl;
+  tree result = tsubst_expr (expr, args, complain, in_decl, false);
+  --processing_template_decl;
+  return build_nt (PRED_CONSTR, result);
+}
+
+/* Substitute into a check constraint. */
+
+tree
+tsubst_check_constraint (tree t, tree args,
+                         tsubst_flags_t complain, tree in_decl)
+{
+  tree decl = CHECK_CONSTR_CONCEPT (t);
+  tree tmpl = DECL_TI_TEMPLATE (decl);
+  tree targs = CHECK_CONSTR_ARGS (t);
+
+  /* Substitute through by building an template-id expression
+     and then substituting into that. */
+  tree expr = build_nt (TEMPLATE_ID_EXPR, tmpl, targs);
+  ++processing_template_decl;
+  tree result = tsubst_expr (expr, args, complain, in_decl, false);
+  --processing_template_decl;
+
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  /* Extract the results and rebuild the check constraint. */
+  decl = DECL_TEMPLATE_RESULT (TREE_OPERAND (result, 0));
+  args = TREE_OPERAND (result, 1);
+
+  return build_nt (CHECK_CONSTR, decl, args);
+}
+
+/* Substitute into the conjunction of constraints. Returns
+   error_mark_node if substitution into either operand fails. */
+
+tree
+tsubst_logical_operator (tree t, tree args,
+			 tsubst_flags_t complain, tree in_decl)
+{
+  tree t0 = TREE_OPERAND (t, 0);
+  tree r0 = tsubst_constraint (t0, args, complain, in_decl);
+  if (r0 == error_mark_node)
+    return error_mark_node;
+  tree t1 = TREE_OPERAND (t, 1);
+  tree r1 = tsubst_constraint (t1, args, complain, in_decl);
+  if (r1 == error_mark_node)
+    return error_mark_node;
+  return build_nt (TREE_CODE (t), r0, r1);
+}
+
+namespace {
+
+/* Substitute ARGS into the expression constraint T.  */
+
+tree
+tsubst_expr_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  cp_unevaluated guard;
+  tree expr = EXPR_CONSTR_EXPR (t);
+  tree ret = tsubst_expr (expr, args, complain, in_decl, false);
+  if (ret == error_mark_node)
+    return error_mark_node;
+  return build_nt (EXPR_CONSTR, ret);
+}
+
+/* Substitute ARGS into the type constraint T.  */
+
+tree
+tsubst_type_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  tree type = TYPE_CONSTR_TYPE (t);
+  tree ret = tsubst (type, args, complain, in_decl);
+  if (ret == error_mark_node)
+    return error_mark_node;
+  return build_nt (TYPE_CONSTR, ret);
+}
+
+/* Substitute ARGS into the implicit conversion constraint T.  */
+
+tree
+tsubst_implicit_conversion_constr (tree t, tree args, tsubst_flags_t complain,
+                                   tree in_decl)
+{
+  cp_unevaluated guard;
+  tree expr = ICONV_CONSTR_EXPR (t);
+  tree type = ICONV_CONSTR_TYPE (t);
+  tree new_expr = tsubst_expr (expr, args, complain, in_decl, false);
+  if (new_expr == error_mark_node)
+    return error_mark_node;
+  tree new_type = tsubst (type, args, complain, in_decl);
+  if (new_type == error_mark_node)
+    return error_mark_node;
+  return build_nt (ICONV_CONSTR, new_expr, new_type);
+}
+
+/* Substitute ARGS into the argument deduction constraint T.  */
+
+tree
+tsubst_argument_deduction_constr (tree t, tree args, tsubst_flags_t complain,
+                                  tree in_decl)
+{
+  cp_unevaluated guard;
+  tree expr = DEDUCT_CONSTR_EXPR (t);
+  tree pattern = DEDUCT_CONSTR_PATTERN (t);
+  tree autos = DEDUCT_CONSTR_PLACEHOLDER(t);
+  tree new_expr = tsubst_expr (expr, args, complain, in_decl, false);
+  if (new_expr == error_mark_node)
+    return error_mark_node;
+  /* It seems like substituting through the pattern will not affect the
+     placeholders.  We should (?) be able to reuse the existing list
+     without any problems.  If not, then we probably want to create a
+     new list of placeholders and then instantiate the pattern using
+     those.  */
+  tree new_pattern = tsubst (pattern, args, complain, in_decl);
+  if (new_pattern == error_mark_node)
+    return error_mark_node;
+  return build_nt (DEDUCT_CONSTR, new_expr, new_pattern, autos);
+}
+
+/* Substitute ARGS into the exception constraint T.  */
+
+tree
+tsubst_exception_constr (tree t, tree args, tsubst_flags_t complain,
+			 tree in_decl)
+{
+  cp_unevaluated guard;
+  tree expr = EXCEPT_CONSTR_EXPR (t);
+  tree ret = tsubst_expr (expr, args, complain, in_decl, false);
+  if (ret == error_mark_node)
+    return error_mark_node;
+  return build_nt (EXCEPT_CONSTR, ret);
+}
+
+/* A subroutine of tsubst_constraint_variables. Register local
+   specializations for each of parameter in PARMS and its
+   corresponding substituted constraint variable in VARS.
+   Returns VARS. */
+
+tree
+declare_constraint_vars (tree parms, tree vars)
+{
+  tree s = vars;
+  for (tree t = parms; t; t = DECL_CHAIN (t))
+    {
+      if (DECL_PACK_P (t))
+        {
+          tree pack = extract_fnparm_pack (t, &s);
+          register_local_specialization (pack, t);
+        }
+      else
+        {
+          register_local_specialization (s, t);
+          s = DECL_CHAIN (s);
+        }
+    }
+  return vars;
+}
+
+/* A subroutine of tsubst_parameterized_constraint. Substitute ARGS
+   into the parameter list T, producing a sequence of constraint
+   variables, declared in the current scope.
+
+   Note that the caller must establish a local specialization stack
+   prior to calling this function since this substitution will
+   declare the substituted parameters. */
+
+tree
+tsubst_constraint_variables (tree t, tree args,
+                             tsubst_flags_t complain, tree in_decl)
+{
+  /* Clear cp_unevaluated_operand across tsubst so that we get a proper chain
+     of PARM_DECLs.  */
+  int saved_unevaluated_operand = cp_unevaluated_operand;
+  cp_unevaluated_operand = 0;
+  tree vars = tsubst (t, args, complain, in_decl);
+  cp_unevaluated_operand = saved_unevaluated_operand;
+  if (vars == error_mark_node)
+    return error_mark_node;
+  return declare_constraint_vars (t, vars);
+}
+
+/* Substitute ARGS into the parameterized constraint T.  */
+
+tree
+tsubst_parameterized_constraint (tree t, tree args,
+				 tsubst_flags_t complain, tree in_decl)
+{
+  local_specialization_stack stack;
+  tree vars = tsubst_constraint_variables (PARM_CONSTR_PARMS (t),
+					   args, complain, in_decl);
+  if (vars == error_mark_node)
+    return error_mark_node;
+  tree expr = tsubst_constraint (PARM_CONSTR_OPERAND (t), args,
+				 complain, in_decl);
+  if (expr == error_mark_node)
+    return error_mark_node;
+  return build_nt (PARM_CONSTR, vars, expr);
+}
+
+/* Substitute ARGS into the simple requirement T. Note that
+   substitution may result in an ill-formed expression without
+   causing the program to be ill-formed. In such cases, the
+   requirement wraps an error_mark_node. */
+
+inline tree
+tsubst_simple_requirement (tree t, tree args,
+                           tsubst_flags_t complain, tree in_decl)
+{
+  ++processing_template_decl;
+  tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+  --processing_template_decl;
+  return finish_simple_requirement (expr);
+}
+
+/* Substitute ARGS into the type requirement T. Note that
+   substitution may result in an ill-formed type without
+   causing the program to be ill-formed. In such cases, the
+   requirement wraps an error_mark_node. */
+
+inline tree
+tsubst_type_requirement (tree t, tree args,
+                         tsubst_flags_t complain, tree in_decl)
+{
+  ++processing_template_decl;
+  tree type = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl);
+  --processing_template_decl;
+  return finish_type_requirement (type);
+}
+
+/* Substitute args into the compound requirement T. If substituting
+   into either the expression or the type fails, the corresponding
+   operands in the resulting node will be error_mark_node. This
+   preserves a requirement for the purpose of partial ordering, but
+   it will never be satisfied. */
+
+tree
+tsubst_compound_requirement (tree t, tree args,
+                             tsubst_flags_t complain, tree in_decl)
+{
+  ++processing_template_decl;
+  tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+  tree type = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl);
+  --processing_template_decl;
+  bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
+  return finish_compound_requirement (expr, type, noexcept_p);
+}
+
+/* Substitute ARGS into the nested requirement T. */
+
+tree
+tsubst_nested_requirement (tree t, tree args,
+                           tsubst_flags_t complain, tree in_decl)
+{
+  ++processing_template_decl;
+  tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+  --processing_template_decl;
+  return finish_nested_requirement (expr);
+}
+
+/* Substitute ARGS into the requirement T.  */
+
+inline tree
+tsubst_requirement (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  switch (TREE_CODE (t))
+    {
+    case SIMPLE_REQ:
+      return tsubst_simple_requirement (t, args, complain, in_decl);
+    case TYPE_REQ:
+      return tsubst_type_requirement (t, args, complain, in_decl);
+    case COMPOUND_REQ:
+      return tsubst_compound_requirement (t, args, complain, in_decl);
+    case NESTED_REQ:
+      return tsubst_nested_requirement (t, args, complain, in_decl);
+    default:
+      gcc_unreachable ();
+    }
+  return error_mark_node;
+}
+
+/* Substitute ARGS into the list of requirements T. Note that
+   substitution failures here result in ill-formed programs. */
+
+tree
+tsubst_requirement_body (tree t, tree args,
+                         tsubst_flags_t complain, tree in_decl)
+{
+  tree r = NULL_TREE;
+  while (t)
+    {
+      tree e = tsubst_requirement (TREE_VALUE (t), args, complain, in_decl);
+      if (e == error_mark_node)
+        return error_mark_node;
+      r = tree_cons (NULL_TREE, e, r);
+      t = TREE_CHAIN (t);
+    }
+  /* Ensure that the order of constraints is the same as the original.  */
+  return nreverse (r);
+}
+
+} /* namespace */
+
+/* Substitute ARGS into the requires expression T. Note that this
+   results in the re-declaration of local parameters when
+   substituting through the parameter list. If either substitution
+   fails, the program is ill-formed. */
+
+tree
+tsubst_requires_expr (tree t, tree args,
+                      tsubst_flags_t complain, tree in_decl)
+{
+  local_specialization_stack stack;
+
+  tree parms = TREE_OPERAND (t, 0);
+  if (parms)
+    {
+      parms = tsubst_constraint_variables (parms, args, complain, in_decl);
+      if (parms == error_mark_node)
+        return error_mark_node;
+    }
+
+  tree reqs = TREE_OPERAND (t, 1);
+  reqs = tsubst_requirement_body (reqs, args, complain, in_decl);
+  if (reqs == error_mark_node)
+    return error_mark_node;
+
+  return finish_requires_expr (parms, reqs);
+}
+
+/* Substitute ARGS into the constraint information CI, producing a new
+   constraint record. */
+
+tree
+tsubst_constraint_info (tree t, tree args,
+                        tsubst_flags_t complain, tree in_decl)
+{
+  if (!t || t == error_mark_node || !check_constraint_info (t))
+    return NULL_TREE;
+
+  tree tmpl_constr = NULL_TREE;
+  if (tree r = CI_TEMPLATE_REQS (t))
+    tmpl_constr = tsubst_constraint (r, args, complain, in_decl);
+
+  tree decl_constr = NULL_TREE;
+  if (tree r = CI_DECLARATOR_REQS (t))
+    decl_constr = tsubst_constraint (r, args, complain, in_decl);
+
+  return build_constraints (tmpl_constr, decl_constr);
+}
+
+/* Substitute ARGS into the constraint T. */
+
+tree
+tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  if (t == NULL_TREE)
+    return t;
+  switch (TREE_CODE (t))
+  {
+  case PRED_CONSTR:
+    return tsubst_predicate_constraint (t, args, complain, in_decl);
+  case CHECK_CONSTR:
+    return tsubst_check_constraint (t, args, complain, in_decl);
+  case CONJ_CONSTR:
+  case DISJ_CONSTR:
+    return tsubst_logical_operator (t, args, complain, in_decl);
+  case PARM_CONSTR:
+    return tsubst_parameterized_constraint (t, args, complain, in_decl);
+  case EXPR_CONSTR:
+    return tsubst_expr_constr (t, args, complain, in_decl);
+  case TYPE_CONSTR:
+    return tsubst_type_constr (t, args, complain, in_decl);
+  case ICONV_CONSTR:
+    return tsubst_implicit_conversion_constr (t, args, complain, in_decl);
+  case DEDUCT_CONSTR:
+    return tsubst_argument_deduction_constr (t, args, complain, in_decl);
+  case EXCEPT_CONSTR:
+    return tsubst_exception_constr (t, args, complain, in_decl);
+  default:
+    gcc_unreachable ();
+  }
+  return error_mark_node;
+}
+
+/*---------------------------------------------------------------------------
+                        Constraint satisfaction
+---------------------------------------------------------------------------*/
+
+/* The following functions determine if a constraint, when
+   substituting template arguments, is satisfied. For convenience,
+   satisfaction reduces a constraint to either true or false (and
+   nothing else). */
+
+namespace {
+
+tree satisfy_constraint_1 (tree, tree, tsubst_flags_t, tree);
+
+/* Check the constraint pack expansion.  */
+
+tree
+satisfy_pack_expansion (tree t, tree args,
+                      tsubst_flags_t complain, tree in_decl)
+{
+  /* Get the vector of satisfaction results.
+     gen_elem_of_pack_expansion_instantiation will check that each element of
+     the expansion is satisfied.  */
+  tree exprs = tsubst_pack_expansion (t, args, complain, in_decl);
+
+  if (exprs == error_mark_node)
+    return boolean_false_node;
+
+  /* TODO: It might be better to normalize each expanded term
+     and evaluate them separately. That would provide better
+     opportunities for diagnostics.  */
+  for (int i = 0; i < TREE_VEC_LENGTH (exprs); ++i)
+    if (TREE_VEC_ELT (exprs, i) != boolean_true_node)
+      return boolean_false_node;
+  return boolean_true_node;
+}
+
+/* A predicate constraint is satisfied if its expression evaluates
+   to true. If substitution into that node fails, the constraint
+   is not satisfied ([temp.constr.pred]).
+
+   Note that a predicate constraint is a constraint expression
+   of type bool. If neither of those are true, the program is
+   ill-formed; they are not SFINAE'able errors. */
+
+tree
+satisfy_predicate_constraint (tree t, tree args,
+                              tsubst_flags_t complain, tree in_decl)
+{
+  tree expr = TREE_OPERAND (t, 0);
+
+  /* We should never have a naked pack expansion in a predicate constraint.  */
+  gcc_assert (TREE_CODE (expr) != EXPR_PACK_EXPANSION);
+
+  /* If substitution into the expression fails, the constraint
+     is not satisfied.  */
+  expr = tsubst_expr (expr, args, complain, in_decl, false);
+  if (expr == error_mark_node)
+    return boolean_false_node;
+
+  /* A predicate constraint shall have type bool. In some
+     cases, substitution gives us const-qualified bool, which
+     is also acceptable.  */
+  tree type = cv_unqualified (TREE_TYPE (expr));
+  if (!same_type_p (type, boolean_type_node))
+    {
+      error_at (EXPR_LOC_OR_LOC (expr, input_location),
+                "constraint %qE does not have type %qT",
+                expr, boolean_type_node);
+      return boolean_false_node;
+    }
+
+  return cxx_constant_value (expr);
+}
+
+/* A concept check constraint like C<CARGS> is satisfied if substituting ARGS
+   into CARGS succeeds and C is satisfied for the resulting arguments.  */
+
+tree
+satisfy_check_constraint (tree t, tree args,
+                          tsubst_flags_t complain, tree in_decl)
+{
+  tree decl = CHECK_CONSTR_CONCEPT (t);
+  tree tmpl = DECL_TI_TEMPLATE (decl);
+  tree cargs = CHECK_CONSTR_ARGS (t);
+
+  /* Instantiate the concept check arguments.  */
+  tree targs = tsubst (cargs, args, tf_none, NULL_TREE);
+  if (targs == error_mark_node)
+    return boolean_false_node;
+
+  /* Search for a previous value.  */
+  if (tree prev = lookup_concept_satisfaction (tmpl, targs))
+    return prev;
+
+  /* Expand the concept; failure here implies non-satisfaction.  */
+  tree def = expand_concept (decl, targs);
+  if (def == error_mark_node)
+    return memoize_concept_satisfaction (tmpl, args, boolean_false_node);
+
+  /* Recursively satisfy the constraint.  */
+  tree result = satisfy_constraint_1 (def, targs, complain, in_decl);
+  return memoize_concept_satisfaction (tmpl, targs, result);
+}
+
+/* Check an expression constraint. The constraint is satisfied if
+   substitution succeeds ([temp.constr.expr]).
+
+   Note that the expression is unevaluated. */
+
+tree
+satisfy_expression_constraint (tree t, tree args,
+                               tsubst_flags_t complain, tree in_decl)
+{
+  cp_unevaluated guard;
+  deferring_access_check_sentinel deferring;
+
+  tree expr = EXPR_CONSTR_EXPR (t);
+  tree check = tsubst_expr (expr, args, complain, in_decl, false);
+  if (check == error_mark_node)
+    return boolean_false_node;
+  if (!perform_deferred_access_checks (tf_none))
+    return boolean_false_node;
+  return boolean_true_node;
+}
+
+/* Check a type constraint. The constraint is satisfied if
+   substitution succeeds. */
+
+inline tree
+satisfy_type_constraint (tree t, tree args,
+                         tsubst_flags_t complain, tree in_decl)
+{
+  deferring_access_check_sentinel deferring;
+  tree type = TYPE_CONSTR_TYPE (t);
+  gcc_assert (TYPE_P (type) || type == error_mark_node);
+  tree check = tsubst (type, args, complain, in_decl);
+  if (error_operand_p (check))
+    return boolean_false_node;
+  if (!perform_deferred_access_checks (complain))
+    return boolean_false_node;
+  return boolean_true_node;
+}
+
+/* Check an implicit conversion constraint.  */
+
+tree
+satisfy_implicit_conversion_constraint (tree t, tree args,
+                                        tsubst_flags_t complain, tree in_decl)
+{
+  /* Don't tsubst as if we're processing a template. If we try
+     to we can end up generating template-like expressions
+     (e.g., modop-exprs) that aren't properly typed.  */
+  tree expr =
+    tsubst_expr (ICONV_CONSTR_EXPR (t), args, complain, in_decl, false);
+  if (expr == error_mark_node)
+    return boolean_false_node;
+
+  /* Get the transformed target type.  */
+  tree type = tsubst (ICONV_CONSTR_TYPE (t), args, complain, in_decl);
+  if (type == error_mark_node)
+    return boolean_false_node;
+
+  /* Attempt the conversion as a direct initialization
+     of the form TYPE <unspecified> = EXPR.  */
+  tree conv =
+    perform_direct_initialization_if_possible (type, expr, false, complain);
+  if (conv == NULL_TREE || conv == error_mark_node)
+    return boolean_false_node;
+  else
+    return boolean_true_node;
+}
+
+/* Check an argument deduction constraint. */
+
+tree
+satisfy_argument_deduction_constraint (tree t, tree args,
+                                       tsubst_flags_t complain, tree in_decl)
+{
+  /* Substitute through the expression. */
+  tree expr = DEDUCT_CONSTR_EXPR (t);
+  tree init = tsubst_expr (expr, args, complain, in_decl, false);
+  if (expr == error_mark_node)
+    return boolean_false_node;
+
+  /* Perform auto or decltype(auto) deduction to get the result. */
+  tree pattern = DEDUCT_CONSTR_PATTERN (t);
+  tree placeholder = DEDUCT_CONSTR_PLACEHOLDER (t);
+  tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder);
+  tree type_canonical = TYPE_CANONICAL (placeholder);
+  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder)
+    = tsubst_constraint (constr, args, complain|tf_partial, in_decl);
+  TYPE_CANONICAL (placeholder) = NULL_TREE;
+  tree type = do_auto_deduction (pattern, init, placeholder,
+                                 complain, adc_requirement);
+  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = constr;
+  TYPE_CANONICAL (placeholder) = type_canonical;
+  if (type == error_mark_node)
+    return boolean_false_node;
+
+  return boolean_true_node;
+}
+
+/* Check an exception constraint. An exception constraint for an
+   expression e is satisfied when noexcept(e) is true. */
+
+tree
+satisfy_exception_constraint (tree t, tree args,
+                              tsubst_flags_t complain, tree in_decl)
+{
+  tree expr = EXCEPT_CONSTR_EXPR (t);
+  tree check = tsubst_expr (expr, args, complain, in_decl, false);
+  if (check == error_mark_node)
+    return boolean_false_node;
+
+  if (expr_noexcept_p (check, complain))
+    return boolean_true_node;
+  else
+    return boolean_false_node;
+}
+
+/* Check a parameterized constraint. */
+
+tree
+satisfy_parameterized_constraint (tree t, tree args,
+                                  tsubst_flags_t complain, tree in_decl)
+{
+  local_specialization_stack stack;
+  tree parms = PARM_CONSTR_PARMS (t);
+  tree vars = tsubst_constraint_variables (parms, args, complain, in_decl);
+  if (vars == error_mark_node)
+    return boolean_false_node;
+  tree constr = PARM_CONSTR_OPERAND (t);
+  return satisfy_constraint_1 (constr, args, complain, in_decl);
+}
+
+/* Check that the conjunction of constraints is satisfied. Note
+   that if left operand is not satisfied, the right operand
+   is not checked.
+
+   FIXME: Check that this wouldn't result in a user-defined
+   operator. Note that this error is partially diagnosed in
+   satisfy_predicate_constraint. It would be nice to diagnose
+   the overload, but I don't think it's strictly necessary.  */
+
+tree
+satisfy_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl);
+  if (t0 == boolean_false_node)
+    return boolean_false_node;
+  return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl);
+}
+
+/* Check that the disjunction of constraints is satisfied. Note
+   that if the left operand is satisfied, the right operand is not
+   checked.  */
+
+tree
+satisfy_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl);
+  if (t0 == boolean_true_node)
+    return boolean_true_node;
+  return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl);
+}
+
+/* Dispatch to an appropriate satisfaction routine depending on the
+   tree code of T.  */
+
+tree
+satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  gcc_assert (!processing_template_decl);
+
+  if (!t)
+    return boolean_false_node;
+
+  if (t == error_mark_node)
+    return boolean_false_node;
+
+  switch (TREE_CODE (t))
+  {
+  case PRED_CONSTR:
+    return satisfy_predicate_constraint (t, args, complain, in_decl);
+
+  case CHECK_CONSTR:
+    return satisfy_check_constraint (t, args, complain, in_decl);
+
+  case EXPR_CONSTR:
+    return satisfy_expression_constraint (t, args, complain, in_decl);
+
+  case TYPE_CONSTR:
+    return satisfy_type_constraint (t, args, complain, in_decl);
+
+  case ICONV_CONSTR:
+    return satisfy_implicit_conversion_constraint (t, args, complain, in_decl);
+
+  case DEDUCT_CONSTR:
+    return satisfy_argument_deduction_constraint (t, args, complain, in_decl);
+
+  case EXCEPT_CONSTR:
+    return satisfy_exception_constraint (t, args, complain, in_decl);
+
+  case PARM_CONSTR:
+    return satisfy_parameterized_constraint (t, args, complain, in_decl);
+
+  case CONJ_CONSTR:
+    return satisfy_conjunction (t, args, complain, in_decl);
+
+  case DISJ_CONSTR:
+    return satisfy_disjunction (t, args, complain, in_decl);
+
+  case EXPR_PACK_EXPANSION:
+    return satisfy_pack_expansion (t, args, complain, in_decl);
+
+  default:
+    gcc_unreachable ();
+  }
+  return boolean_false_node;
+}
+
+/* Check that the constraint is satisfied, according to the rules
+   for that constraint. Note that each satisfy_* function returns
+   true or false, depending on whether it is satisfied or not.  */
+
+tree
+satisfy_constraint (tree t, tree args)
+{
+  auto_timevar time (TV_CONSTRAINT_SAT);
+
+  /* Turn off template processing. Constraint satisfaction only applies
+     to non-dependent terms, so we want to ensure full checking here.  */
+  processing_template_decl_sentinel proc (true);
+
+  /* Avoid early exit in tsubst and tsubst_copy from null args; since earlier
+     substitution was done with processing_template_decl forced on, there will
+     be expressions that still need semantic processing, possibly buried in
+     decltype or a template argument.  */
+  if (args == NULL_TREE)
+    args = make_tree_vec (1);
+
+  return satisfy_constraint_1 (t, args, tf_none, NULL_TREE);
+}
+
+/* Check the associated constraints in CI against the given
+   ARGS, returning true when the constraints are satisfied
+   and false otherwise.  */
+
+tree
+satisfy_associated_constraints (tree ci, tree args)
+{
+  /* If there are no constraints then this is trivially satisfied. */
+  if (!ci)
+    return boolean_true_node;
+
+  /* If any arguments depend on template parameters, we can't
+     check constraints. */
+  if (args && uses_template_parms (args))
+    return boolean_true_node;
+
+  /* Check if we've seen a previous result. */
+  if (tree prev = lookup_constraint_satisfaction (ci, args))
+    return prev;
+
+  /* Actually test for satisfaction. */
+  tree result = satisfy_constraint (CI_ASSOCIATED_CONSTRAINTS (ci), args);
+  return memoize_constraint_satisfaction (ci, args, result);
+}
+
+} /* namespace */
+
+/* Evaluate the given constraint, returning boolean_true_node
+   if the constraint is satisfied and boolean_false_node
+   otherwise. */
+
+tree
+evaluate_constraints (tree constr, tree args)
+{
+  gcc_assert (constraint_p (constr));
+  return satisfy_constraint (constr, args);
+}
+
+/* Evaluate the function concept FN by substituting its own args
+   into its definition and evaluating that as the result. Returns
+   boolean_true_node if the constraints are satisfied and
+   boolean_false_node otherwise.  */
+
+tree
+evaluate_function_concept (tree fn, tree args)
+{
+  tree constr = build_nt (CHECK_CONSTR, fn, args);
+  return satisfy_constraint (constr, args);
+}
+
+/* Evaluate the variable concept VAR by substituting its own args into
+   its initializer and checking the resulting constraint. Returns
+   boolean_true_node if the constraints are satisfied and
+   boolean_false_node otherwise.  */
+
+tree
+evaluate_variable_concept (tree var, tree args)
+{
+  tree constr = build_nt (CHECK_CONSTR, var, args);
+  return satisfy_constraint (constr, args);
+}
+
+/* Evaluate the given expression as if it were a predicate
+   constraint. Returns boolean_true_node if the constraint
+   is satisfied and boolean_false_node otherwise. */
+
+tree
+evaluate_constraint_expression (tree expr, tree args)
+{
+  tree constr = normalize_expression (expr);
+  return satisfy_constraint (constr, args);
+}
+
+/* Returns true if the DECL's constraints are satisfied.
+   This is used in cases where a declaration is formed but
+   before it is used (e.g., overload resolution). */
+
+bool
+constraints_satisfied_p (tree decl)
+{
+  /* Get the constraints to check for satisfaction. This depends
+     on whether we're looking at a template specialization or not. */
+  tree ci;
+  tree args = NULL_TREE;
+  if (tree ti = DECL_TEMPLATE_INFO (decl))
+    {
+      tree tmpl = TI_TEMPLATE (ti);
+      ci = get_constraints (tmpl);
+      int depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
+      args = get_innermost_template_args (TI_ARGS (ti), depth);
+    }
+  else
+    {
+      ci = get_constraints (decl);
+    }
+
+  tree eval = satisfy_associated_constraints (ci, args);
+  return eval == boolean_true_node;
+}
+
+/* Returns true if the constraints are satisfied by ARGS.
+   Here, T can be either a constraint or a constrained
+   declaration.  */
+
+bool
+constraints_satisfied_p (tree t, tree args)
+{
+  tree eval;
+  if (constraint_p (t))
+    eval = evaluate_constraints (t, args);
+  else
+    eval = satisfy_associated_constraints (get_constraints (t), args);
+  return eval == boolean_true_node;
+}
+
+namespace
+{
+
+/* Normalize EXPR and determine if the resulting constraint is
+   satisfied by ARGS. Returns true if and only if the constraint
+   is satisfied.  This is used extensively by diagnostics to
+   determine causes for failure.  */
+
+inline bool
+constraint_expression_satisfied_p (tree expr, tree args)
+{
+  return evaluate_constraint_expression (expr, args) == boolean_true_node;
+}
+
+} /* namespace */
+
+/*---------------------------------------------------------------------------
+                Semantic analysis of requires-expressions
+---------------------------------------------------------------------------*/
+
+/* Finish a requires expression for the given PARMS (possibly
+   null) and the non-empty sequence of requirements. */
+tree
+finish_requires_expr (tree parms, tree reqs)
+{
+  /* Modify the declared parameters by removing their context
+     so they don't refer to the enclosing scope and explicitly
+     indicating that they are constraint variables. */
+  for (tree parm = parms; parm; parm = DECL_CHAIN (parm))
+    {
+      DECL_CONTEXT (parm) = NULL_TREE;
+      CONSTRAINT_VAR_P (parm) = true;
+    }
+
+  /* Build the node. */
+  tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs);
+  TREE_SIDE_EFFECTS (r) = false;
+  TREE_CONSTANT (r) = true;
+  return r;
+}
+
+/* Construct a requirement for the validity of EXPR. */
+tree
+finish_simple_requirement (tree expr)
+{
+  return build_nt (SIMPLE_REQ, expr);
+}
+
+/* Construct a requirement for the validity of TYPE. */
+tree
+finish_type_requirement (tree type)
+{
+  return build_nt (TYPE_REQ, type);
+}
+
+/* Construct a requirement for the validity of EXPR, along with
+   its properties. if TYPE is non-null, then it specifies either
+   an implicit conversion or argument deduction constraint,
+   depending on whether any placeholders occur in the type name.
+   NOEXCEPT_P is true iff the noexcept keyword was specified. */
+tree
+finish_compound_requirement (tree expr, tree type, bool noexcept_p)
+{
+  tree req = build_nt (COMPOUND_REQ, expr, type);
+  COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p;
+  return req;
+}
+
+/* Finish a nested requirement. */
+tree
+finish_nested_requirement (tree expr)
+{
+  return build_nt (NESTED_REQ, expr);
+}
+
+// Check that FN satisfies the structural requirements of a
+// function concept definition.
+tree
+check_function_concept (tree fn)
+{
+  // Check that the function is comprised of only a single
+  // return statement.
+  tree body = DECL_SAVED_TREE (fn);
+  if (TREE_CODE (body) == BIND_EXPR)
+    body = BIND_EXPR_BODY (body);
+
+  // Sometimes a function call results in the creation of clean up
+  // points. Allow these to be preserved in the body of the
+  // constraint, as we might actually need them for some constexpr
+  // evaluations.
+  if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
+    body = TREE_OPERAND (body, 0);
+
+  /* Check that the definition is written correctly. */
+  if (TREE_CODE (body) != RETURN_EXPR)
+    {
+      location_t loc = DECL_SOURCE_LOCATION (fn);
+      if (TREE_CODE (body) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (body))
+	{
+	  if (seen_error ())
+	    /* The definition was probably erroneous, not empty.  */;
+	  else
+	    error_at (loc, "definition of concept %qD is empty", fn);
+	}
+      else
+        error_at (loc, "definition of concept %qD has multiple statements", fn);
+    }
+
+  return NULL_TREE;
+}
+
+
+// Check that a constrained friend declaration function declaration,
+// FN, is admissible. This is the case only when the declaration depends
+// on template parameters and does not declare a specialization.
+void
+check_constrained_friend (tree fn, tree reqs)
+{
+  if (fn == error_mark_node)
+    return;
+  gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+  // If there are not constraints, this cannot be an error.
+  if (!reqs)
+    return;
+
+  // Constrained friend functions that don't depend on template
+  // arguments are effectively meaningless.
+  if (!uses_template_parms (TREE_TYPE (fn)))
+    {
+      error_at (location_of (fn),
+		"constrained friend does not depend on template parameters");
+      return;
+    }
+}
+
+/*---------------------------------------------------------------------------
+                        Equivalence of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true when A and B are equivalent constraints.  */
+bool
+equivalent_constraints (tree a, tree b)
+{
+  gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO);
+  gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO);
+  return cp_tree_equal (a, b);
+}
+
+/* Returns true if the template declarations A and B have equivalent
+   constraints. This is the case when A's constraints subsume B's and
+   when B's also constrain A's.  */
+bool
+equivalently_constrained (tree d1, tree d2)
+{
+  gcc_assert (TREE_CODE (d1) == TREE_CODE (d2));
+  return equivalent_constraints (get_constraints (d1), get_constraints (d2));
+}
+
+/*---------------------------------------------------------------------------
+                     Partial ordering of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true when the the constraints in A subsume those in B.  */
+
+bool
+subsumes_constraints (tree a, tree b)
+{
+  gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO);
+  gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO);
+  return subsumes (a, b);
+}
+
+/* Returns true when the the constraints in A subsume those in B, but
+   the constraints in B do not subsume the constraints in A.  */
+
+bool
+strictly_subsumes (tree a, tree b)
+{
+  return subsumes (a, b) && !subsumes (b, a);
+}
+
+/* Determines which of the declarations, A or B, is more constrained.
+   That is, which declaration's constraints subsume but are not subsumed
+   by the other's?
+
+   Returns 1 if A is more constrained than B, -1 if B is more constrained
+   than A, and 0 otherwise. */
+
+int
+more_constrained (tree d1, tree d2)
+{
+  tree c1 = get_constraints (d1);
+  tree c2 = get_constraints (d2);
+  int winner = 0;
+  if (subsumes_constraints (c1, c2))
+    ++winner;
+  if (subsumes_constraints (c2, c1))
+    --winner;
+  return winner;
+}
+
+/* Returns true if D1 is at least as constrained as D2. That is, the
+   associated constraints of D1 subsume those of D2, or both declarations
+   are unconstrained. */
+
+bool
+at_least_as_constrained (tree d1, tree d2)
+{
+  tree c1 = get_constraints (d1);
+  tree c2 = get_constraints (d2);
+  return subsumes_constraints (c1, c2);
+}
+
+
+/*---------------------------------------------------------------------------
+                        Constraint diagnostics
+
+FIXME: Normalize expressions into constraints before evaluating them.
+This should be the general pattern for all such diagnostics.
+---------------------------------------------------------------------------*/
+
+/* The number of detailed constraint failures.  */
+
+int constraint_errors = 0;
+
+/* Do not generate errors after diagnosing this number of constraint
+   failures.
+
+   FIXME: This is a really arbitrary number. Provide better control of
+   constraint diagnostics with a command line option.  */
+
+int constraint_thresh = 20;
+
+
+/* Returns true if we should elide the diagnostic for a constraint failure.
+   This is the case when the number of errors has exceeded the pre-configured
+   threshold.  */
+
+inline bool
+elide_constraint_failure_p ()
+{
+  bool ret = constraint_thresh <= constraint_errors;
+  ++constraint_errors;
+  return ret;
+}
+
+/* Returns the number of undiagnosed errors. */
+
+inline int
+undiagnosed_constraint_failures ()
+{
+  return constraint_errors - constraint_thresh;
+}
+
+/* The diagnosis of constraints performs a combination of normalization
+   and satisfaction testing. We recursively walk through the conjunction or
+   disjunction of associated constraints, testing each sub-constraint in
+   turn.  */
+
+namespace {
+
+void diagnose_constraint (location_t, tree, tree, tree);
+
+/* Emit a specific diagnostics for a failed trait.  */
+
+void
+diagnose_trait_expression (location_t loc, tree, tree cur, tree args)
+{
+  if (constraint_expression_satisfied_p (cur, args))
+    return;
+  if (elide_constraint_failure_p())
+    return;
+
+  tree expr = PRED_CONSTR_EXPR (cur);
+  ++processing_template_decl;
+  expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false);
+  --processing_template_decl;
+
+  tree t1 = TRAIT_EXPR_TYPE1 (expr);
+  tree t2 = TRAIT_EXPR_TYPE2 (expr);
+  switch (TRAIT_EXPR_KIND (expr))
+    {
+    case CPTK_HAS_NOTHROW_ASSIGN:
+      inform (loc, "  %qT is not nothrow copy assignable", t1);
+      break;
+    case CPTK_HAS_NOTHROW_CONSTRUCTOR:
+      inform (loc, "  %qT is not nothrow default constructible", t1);
+      break;
+    case CPTK_HAS_NOTHROW_COPY:
+      inform (loc, "  %qT is not nothrow copy constructible", t1);
+      break;
+    case CPTK_HAS_TRIVIAL_ASSIGN:
+      inform (loc, "  %qT is not trivially copy assignable", t1);
+      break;
+    case CPTK_HAS_TRIVIAL_CONSTRUCTOR:
+      inform (loc, "  %qT is not trivially default constructible", t1);
+      break;
+    case CPTK_HAS_TRIVIAL_COPY:
+      inform (loc, "  %qT is not trivially copy constructible", t1);
+      break;
+    case CPTK_HAS_TRIVIAL_DESTRUCTOR:
+      inform (loc, "  %qT is not trivially destructible", t1);
+      break;
+    case CPTK_HAS_VIRTUAL_DESTRUCTOR:
+      inform (loc, "  %qT does not have a virtual destructor", t1);
+      break;
+    case CPTK_IS_ABSTRACT:
+      inform (loc, "  %qT is not an abstract class", t1);
+      break;
+    case CPTK_IS_BASE_OF:
+      inform (loc, "  %qT is not a base of %qT", t1, t2);
+      break;
+    case CPTK_IS_CLASS:
+      inform (loc, "  %qT is not a class", t1);
+      break;
+    case CPTK_IS_EMPTY:
+      inform (loc, "  %qT is not an empty class", t1);
+      break;
+    case CPTK_IS_ENUM:
+      inform (loc, "  %qT is not an enum", t1);
+      break;
+    case CPTK_IS_FINAL:
+      inform (loc, "  %qT is not a final class", t1);
+      break;
+    case CPTK_IS_LITERAL_TYPE:
+      inform (loc, "  %qT is not a literal type", t1);
+      break;
+    case CPTK_IS_POD:
+      inform (loc, "  %qT is not a POD type", t1);
+      break;
+    case CPTK_IS_POLYMORPHIC:
+      inform (loc, "  %qT is not a polymorphic type", t1);
+      break;
+    case CPTK_IS_SAME_AS:
+      inform (loc, "  %qT is not the same as %qT", t1, t2);
+      break;
+    case CPTK_IS_STD_LAYOUT:
+      inform (loc, "  %qT is not an standard layout type", t1);
+      break;
+    case CPTK_IS_TRIVIAL:
+      inform (loc, "  %qT is not a trivial type", t1);
+      break;
+    case CPTK_IS_UNION:
+      inform (loc, "  %qT is not a union", t1);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Diagnose the expression of a predicate constraint.  */
+
+void
+diagnose_other_expression (location_t loc, tree, tree cur, tree args)
+{
+  if (constraint_expression_satisfied_p (cur, args))
+    return;
+  if (elide_constraint_failure_p())
+    return;
+  inform (loc, "%qE evaluated to false", cur);
+}
+
+/* Do our best to infer meaning from predicates.  */
+
+inline void
+diagnose_predicate_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  if (TREE_CODE (PRED_CONSTR_EXPR (cur)) == TRAIT_EXPR)
+    diagnose_trait_expression (loc, orig, cur, args);
+  else
+    diagnose_other_expression (loc, orig, cur, args);
+}
+
+/* Diagnose a failed pack expansion, possibly containing constraints.  */
+
+void
+diagnose_pack_expansion (location_t loc, tree, tree cur, tree args)
+{
+  if (constraint_expression_satisfied_p (cur, args))
+    return;
+  if (elide_constraint_failure_p())
+    return;
+
+  /* Make sure that we don't have naked packs that we don't expect. */
+  if (!same_type_p (TREE_TYPE (cur), boolean_type_node))
+    {
+      inform (loc, "invalid pack expansion in constraint %qE", cur);
+      return;
+    }
+
+  inform (loc, "in the expansion of %qE", cur);
+
+  /* Get the vector of expanded arguments. Note that n must not
+     be 0 since this constraint is not satisfied.  */
+  ++processing_template_decl;
+  tree exprs = tsubst_pack_expansion (cur, args, tf_none, NULL_TREE);
+  --processing_template_decl;
+  if (exprs == error_mark_node)
+    {
+      /* TODO: This error message could be better. */
+      inform (loc, "    substitution failure occurred during expansion");
+      return;
+    }
+
+  /* Check each expanded constraint separately. */
+  int n = TREE_VEC_LENGTH (exprs);
+  for (int i = 0; i < n; ++i)
+    {
+      tree expr = TREE_VEC_ELT (exprs, i);
+      if (!constraint_expression_satisfied_p (expr, args))
+        inform (loc, "    %qE was not satisfied", expr);
+    }
+}
+
+/* Diagnose a potentially unsatisfied concept check constraint DECL<CARGS>.
+   Parameters are as for diagnose_constraint.  */
+
+void
+diagnose_check_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+
+  tree decl = CHECK_CONSTR_CONCEPT (cur);
+  tree cargs = CHECK_CONSTR_ARGS (cur);
+  tree tmpl = DECL_TI_TEMPLATE (decl);
+  tree check = build_nt (CHECK_CONSTR, decl, cargs);
+
+  /* Instantiate the concept check arguments.  */
+  tree targs = tsubst (cargs, args, tf_none, NULL_TREE);
+  if (targs == error_mark_node)
+    {
+      if (elide_constraint_failure_p ())
+        return;
+      inform (loc, "invalid use of the concept %qE", check);
+      tsubst (cargs, args, tf_warning_or_error, NULL_TREE);
+      return;
+    }
+
+  tree sub = build_tree_list (tmpl, targs);
+  /* Update to the expanded definitions. */
+  cur = expand_concept (decl, targs);
+  if (cur == error_mark_node)
+    {
+      if (elide_constraint_failure_p ())
+        return;
+      inform (loc, "in the expansion of concept %<%E %S%>", check, sub);
+      cur = get_concept_definition (decl);
+      tsubst_expr (cur, targs, tf_warning_or_error, NULL_TREE, false);
+      return;
+    }
+
+  orig = get_concept_definition (CHECK_CONSTR_CONCEPT (orig));
+  orig = normalize_expression (orig);
+
+  location_t dloc = DECL_SOURCE_LOCATION (decl);
+  inform (dloc, "within %qS", sub);
+  diagnose_constraint (dloc, orig, cur, targs);
+}
+
+/* Diagnose a potentially unsatisfied conjunction or disjunction.  Parameters
+   are as for diagnose_constraint.  */
+
+void
+diagnose_logical_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  tree t0 = TREE_OPERAND (cur, 0);
+  tree t1 = TREE_OPERAND (cur, 1);
+  if (!constraints_satisfied_p (t0, args))
+    diagnose_constraint (loc, TREE_OPERAND (orig, 0), t0, args);
+  else if (TREE_CODE (orig) == TRUTH_ORIF_EXPR)
+    return;
+  if (!constraints_satisfied_p (t1, args))
+    diagnose_constraint (loc, TREE_OPERAND (orig, 1), t1, args);
+}
+
+/* Diagnose a potential expression constraint failure. */
+
+void
+diagnose_expression_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+  if (elide_constraint_failure_p())
+    return;
+
+  tree expr = EXPR_CONSTR_EXPR (orig);
+  inform (loc, "the required expression %qE would be ill-formed", expr);
+
+  // TODO: We should have a flag that controls this substitution.
+  // I'm finding it very useful for resolving concept check errors.
+
+  // inform (input_location, "==== BEGIN DUMP ====");
+  // tsubst_expr (EXPR_CONSTR_EXPR (orig), args, tf_warning_or_error, NULL_TREE, false);
+  // inform (input_location, "==== END DUMP ====");
+}
+
+/* Diagnose a potentially failed type constraint. */
+
+void
+diagnose_type_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+  if (elide_constraint_failure_p())
+    return;
+
+  tree type = TYPE_CONSTR_TYPE (orig);
+  inform (loc, "the required type %qT would be ill-formed", type);
+}
+
+/* Diagnose a potentially unsatisfied conversion constraint. */
+
+void
+diagnose_implicit_conversion_constraint (location_t loc, tree orig, tree cur,
+					 tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+
+  /* The expression and type will previously have been substituted into,
+     and therefore may already be an error. Also, we will have already
+     diagnosed substitution failures into an expression since this must be
+     part of a compound requirement.  */
+  tree expr = ICONV_CONSTR_EXPR (cur);
+  if (error_operand_p (expr))
+    return;
+
+  /* Don't elide a previously diagnosed failure.  */
+  if (elide_constraint_failure_p())
+    return;
+
+  tree type = ICONV_CONSTR_TYPE (cur);
+  if (error_operand_p (type))
+    {
+      inform (loc, "substitution into type %qT failed",
+	      ICONV_CONSTR_TYPE (orig));
+      return;
+    }
+
+  inform(loc, "%qE is not implicitly convertible to %qT", expr, type);
+}
+
+/* Diagnose an argument deduction constraint. */
+
+void
+diagnose_argument_deduction_constraint (location_t loc, tree orig, tree cur,
+					tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+
+  /* The expression and type will previously have been substituted into,
+     and therefore may already be an error. Also, we will have already
+     diagnosed substution failures into an expression since this must be
+     part of a compound requirement.  */
+  tree expr = DEDUCT_CONSTR_EXPR (cur);
+  if (error_operand_p (expr))
+    return;
+
+  /* Don't elide a previously diagnosed failure.  */
+  if (elide_constraint_failure_p ())
+    return;
+
+  tree pattern = DEDUCT_CONSTR_PATTERN (cur);
+  if (error_operand_p (pattern))
+    {
+      inform (loc, "substitution into type %qT failed",
+	      DEDUCT_CONSTR_PATTERN (orig));
+      return;
+    }
+
+  inform (loc, "unable to deduce placeholder type %qT from %qE",
+	  pattern, expr);
+}
+
+/* Diagnose an exception constraint. */
+
+void
+diagnose_exception_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+  if (elide_constraint_failure_p ())
+    return;
+
+  /* Rebuild a noexcept expression. */
+  tree expr = EXCEPT_CONSTR_EXPR (cur);
+  if (error_operand_p (expr))
+    return;
+
+  inform (loc, "%qE evaluated to false", EXCEPT_CONSTR_EXPR (orig));
+}
+
+/* Diagnose a potentially unsatisfied parameterized constraint.  */
+
+void
+diagnose_parameterized_constraint (location_t loc, tree orig, tree cur,
+				   tree args)
+{
+  if (constraints_satisfied_p (cur, args))
+    return;
+
+  local_specialization_stack stack;
+  tree parms = PARM_CONSTR_PARMS (cur);
+  tree vars = tsubst_constraint_variables (parms, args, tf_warning_or_error,
+					   NULL_TREE);
+  if (vars == error_mark_node)
+    {
+      if (elide_constraint_failure_p ())
+        return;
+
+      /* TODO: Check which variable failed and use orig to diagnose
+         that substitution error.  */
+      inform (loc, "failed to instantiate constraint variables");
+      return;
+    }
+
+  /* TODO: It would be better write these in a list. */
+  while (vars)
+    {
+      inform (loc, "    with %q#D", vars);
+      vars = TREE_CHAIN (vars);
+    }
+  orig = PARM_CONSTR_OPERAND (orig);
+  cur = PARM_CONSTR_OPERAND (cur);
+  return diagnose_constraint (loc, orig, cur, args);
+}
+
+/* Diagnose the constraint CUR for the given ARGS. This is only ever invoked
+   on the associated constraints, so we can only have conjunctions of
+   predicate constraints.  The ORIGinal (dependent) constructs follow
+   the current constraints to enable better diagnostics.  Note that ORIG
+   and CUR must be the same kinds of node, except when CUR is an error.  */
+
+void
+diagnose_constraint (location_t loc, tree orig, tree cur, tree args)
+{
+  switch (TREE_CODE (cur))
+    {
+    case EXPR_CONSTR:
+      diagnose_expression_constraint (loc, orig, cur, args);
+      break;
+
+    case TYPE_CONSTR:
+      diagnose_type_constraint (loc, orig, cur, args);
+      break;
+
+    case ICONV_CONSTR:
+      diagnose_implicit_conversion_constraint (loc, orig, cur, args);
+      break;
+
+    case DEDUCT_CONSTR:
+      diagnose_argument_deduction_constraint (loc, orig, cur, args);
+      break;
+
+    case EXCEPT_CONSTR:
+      diagnose_exception_constraint (loc, orig, cur, args);
+      break;
+
+    case CONJ_CONSTR:
+    case DISJ_CONSTR:
+      diagnose_logical_constraint (loc, orig, cur, args);
+      break;
+
+    case PRED_CONSTR:
+      diagnose_predicate_constraint (loc, orig, cur, args);
+      break;
+
+    case PARM_CONSTR:
+      diagnose_parameterized_constraint (loc, orig, cur, args);
+      break;
+
+    case CHECK_CONSTR:
+      diagnose_check_constraint (loc, orig, cur, args);
+      break;
+
+    case EXPR_PACK_EXPANSION:
+      diagnose_pack_expansion (loc, orig, cur, args);
+      break;
+
+    case ERROR_MARK:
+      /* TODO: Can we improve the diagnostic with the original?  */
+      inform (input_location, "ill-formed constraint");
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Diagnose the reason(s) why ARGS do not satisfy the constraints
+   of declaration DECL. */
+
+void
+diagnose_declaration_constraints (location_t loc, tree decl, tree args)
+{
+  inform (loc, "  constraints not satisfied");
+
+  /* Constraints are attached to the template.  */
+  if (tree ti = DECL_TEMPLATE_INFO (decl))
+    {
+      decl = TI_TEMPLATE (ti);
+      if (!args)
+	args = TI_ARGS (ti);
+    }
+
+  /* Recursively diagnose the associated constraints.  */
+  tree ci = get_constraints (decl);
+  tree t = CI_ASSOCIATED_CONSTRAINTS (ci);
+  diagnose_constraint (loc, t, t, args);
+}
+
+} // namespace
+
+/* Emit diagnostics detailing the failure ARGS to satisfy the
+   constraints of T. Here, T can be either a constraint
+   or a declaration.  */
+
+void
+diagnose_constraints (location_t loc, tree t, tree args)
+{
+  constraint_errors = 0;
+
+  if (constraint_p (t))
+    diagnose_constraint (loc, t, t, args);
+  else if (DECL_P (t))
+    diagnose_declaration_constraints (loc, t, args);
+  else
+    gcc_unreachable ();
+
+  /* Note the number of elided failures. */
+  int n = undiagnosed_constraint_failures ();
+  if (n > 0)
+    inform (loc, "... and %d more constraint errors not shown", n);
+}