diff gcc/cp/logic.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/logic.cc	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,804 @@
+/* Derivation and subsumption 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"
+#define INCLUDE_LIST
+#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"
+
+namespace {
+
+// Helper algorithms
+
+template<typename I>
+inline I
+next (I iter)
+{
+  return ++iter;
+}
+
+template<typename I, typename P>
+inline bool
+any_p (I first, I last, P pred)
+{
+  while (first != last)
+    {
+      if (pred(*first))
+        return true;
+      ++first;
+    }
+  return false;
+}
+
+bool prove_implication (tree, tree);
+
+/*---------------------------------------------------------------------------
+                           Proof state
+---------------------------------------------------------------------------*/
+
+struct term_entry
+{
+  tree t;
+};
+
+/* Hashing function and equality for constraint entries.  */
+
+struct term_hasher : ggc_ptr_hash<term_entry>
+{
+  static hashval_t hash (term_entry *e)
+  {
+    return iterative_hash_template_arg (e->t, 0);
+  }
+
+  static bool equal (term_entry *e1, term_entry *e2)
+  {
+    return cp_tree_equal (e1->t, e2->t);
+  }
+};
+
+/* A term list is a list of atomic constraints. It is used
+   to maintain the lists of assumptions and conclusions in a
+   proof goal.
+
+   Each term list maintains an iterator that refers to the current
+   term. This can be used by various tactics to support iteration
+   and stateful manipulation of the list. */
+struct term_list
+{
+  typedef std::list<tree>::iterator iterator;
+
+  term_list ();
+  term_list (tree);
+
+  bool includes (tree);
+  iterator insert (iterator, tree);
+  iterator push_back (tree);
+  iterator erase (iterator);
+  iterator replace (iterator, tree);
+  iterator replace (iterator, tree, tree);
+
+  iterator begin() { return seq.begin(); }
+  iterator end() { return seq.end(); }
+
+  std::list<tree>         seq;
+  hash_table<term_hasher> tab;
+};
+
+inline
+term_list::term_list ()
+  : seq(), tab (11)
+{
+}
+
+/* Initialize a term list with an initial term. */
+
+inline
+term_list::term_list (tree t)
+  : seq (), tab (11)
+{
+  push_back (t);
+}
+
+/* Returns true if T is the in the tree. */
+
+inline bool
+term_list::includes (tree t)
+{
+  term_entry ent = {t};
+  return tab.find (&ent);
+}
+
+/* Append a term to the list. */
+inline term_list::iterator
+term_list::push_back (tree t)
+{
+  return insert (end(), t);
+}
+
+/* Insert a new (unseen) term T into the list before the proposition
+   indicated by ITER. Returns the iterator to the newly inserted
+   element.  */
+
+term_list::iterator
+term_list::insert (iterator iter, tree t)
+{
+  gcc_assert (!includes (t));
+  iter = seq.insert (iter, t);
+  term_entry ent = {t};
+  term_entry** slot = tab.find_slot (&ent, INSERT);
+  term_entry* ptr = ggc_alloc<term_entry> ();
+  *ptr = ent;
+  *slot = ptr;
+  return iter;
+}
+
+/* Remove an existing term from the list. Returns an iterator referring
+   to the element after the removed term.  This may be end().  */
+
+term_list::iterator
+term_list::erase (iterator iter)
+{
+  gcc_assert (includes (*iter));
+  term_entry ent = {*iter};
+  tab.remove_elt (&ent);
+  iter = seq.erase (iter);
+  return iter;
+}
+
+/* Replace the given term with that specified. If the term has
+   been previously seen, do not insert the term. Returns the
+   first iterator past the current term.  */
+
+term_list::iterator
+term_list::replace (iterator iter, tree t)
+{
+  iter = erase (iter);
+  if (!includes (t))
+    insert (iter, t);
+  return iter;
+}
+
+
+/* Replace the term at the given position by the supplied T1
+   followed by t2. This is used in certain logical operators to
+   load a list of assumptions or conclusions.  */
+
+term_list::iterator
+term_list::replace (iterator iter, tree t1, tree t2)
+{
+  iter = erase (iter);
+  if (!includes (t1))
+    insert (iter, t1);
+  if (!includes (t2))
+    insert (iter, t2);
+  return iter;
+}
+
+/* A goal (or subgoal) models a sequent of the form
+   'A |- C' where A and C are lists of assumptions and
+   conclusions written as propositions in the constraint
+   language (i.e., lists of trees). */
+
+struct proof_goal
+{
+  term_list assumptions;
+  term_list conclusions;
+};
+
+/* A proof state owns a list of goals and tracks the
+   current sub-goal. The class also provides facilities
+   for managing subgoals and constructing term lists. */
+
+struct proof_state : std::list<proof_goal>
+{
+  proof_state ();
+
+  iterator branch (iterator i);
+  iterator discharge (iterator i);
+};
+
+/* Initialize the state with a single empty goal, and set that goal
+   as the current subgoal.  */
+
+inline
+proof_state::proof_state ()
+  : std::list<proof_goal> (1)
+{ }
+
+
+/* Branch the current goal by creating a new subgoal, returning a
+   reference to the new object. This does not update the current goal. */
+
+inline proof_state::iterator
+proof_state::branch (iterator i)
+{
+  gcc_assert (i != end());
+  proof_goal& g = *i;
+  return insert (++i, g);
+}
+
+/* Discharge the current goal, setting it equal to the
+   next non-satisfied goal. */
+
+inline proof_state::iterator
+proof_state::discharge (iterator i)
+{
+  gcc_assert (i != end());
+  return erase (i);
+}
+
+
+/*---------------------------------------------------------------------------
+                        Debugging
+---------------------------------------------------------------------------*/
+
+// void
+// debug (term_list& ts)
+// {
+//   for (term_list::iterator i = ts.begin(); i != ts.end(); ++i)
+//     verbatim ("  # %E", *i);
+// }
+//
+// void
+// debug (proof_goal& g)
+// {
+//   debug (g.assumptions);
+//   verbatim ("       |-");
+//   debug (g.conclusions);
+// }
+
+/*---------------------------------------------------------------------------
+                        Atomicity of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true if T is not an atomic constraint.  */
+
+bool
+non_atomic_constraint_p (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case PRED_CONSTR:
+    case EXPR_CONSTR:
+    case TYPE_CONSTR:
+    case ICONV_CONSTR:
+    case DEDUCT_CONSTR:
+    case EXCEPT_CONSTR:
+      /* A pack expansion isn't atomic, but it can't decompose to prove an
+	 atom, so it shouldn't cause analyze_atom to return undecided.  */
+    case EXPR_PACK_EXPANSION:
+      return false;
+    case CHECK_CONSTR:
+    case PARM_CONSTR:
+    case CONJ_CONSTR:
+    case DISJ_CONSTR:
+      return true;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Returns true if any constraints in T are not atomic.  */
+
+bool
+any_non_atomic_constraints_p (term_list& t)
+{
+  return any_p (t.begin(), t.end(), non_atomic_constraint_p);
+}
+
+/*---------------------------------------------------------------------------
+                           Proof validations
+---------------------------------------------------------------------------*/
+
+enum proof_result
+{
+  invalid,
+  valid,
+  undecided
+};
+
+proof_result check_term (term_list&, tree);
+
+
+proof_result
+analyze_atom (term_list& ts, tree t)
+{
+  /* FIXME: Hook into special cases, if any. */
+  /*
+  term_list::iterator iter = ts.begin();
+  term_list::iterator end = ts.end();
+  while (iter != end)
+    {
+      ++iter;
+    }
+  */
+
+  if (non_atomic_constraint_p (t))
+    return undecided;
+  if (any_non_atomic_constraints_p (ts))
+    return undecided;
+  return invalid;
+}
+
+/* Search for a pack expansion in the list of assumptions that would
+   make this expansion valid.  */
+
+proof_result
+analyze_pack (term_list& ts, tree t)
+{
+  tree c1 = normalize_expression (PACK_EXPANSION_PATTERN (t));
+  term_list::iterator iter = ts.begin();
+  term_list::iterator end = ts.end();
+  while (iter != end)
+    {
+      if (TREE_CODE (*iter) == TREE_CODE (t))
+        {
+          tree c2 = normalize_expression (PACK_EXPANSION_PATTERN (*iter));
+          if (prove_implication (c2, c1))
+            return valid;
+          else
+            return invalid;
+        }
+      ++iter;
+    }
+  return invalid;
+}
+
+/* Search for concept checks in TS that we know subsume T. */
+
+proof_result
+search_known_subsumptions (term_list& ts, tree t)
+{
+  for (term_list::iterator i = ts.begin(); i != ts.end(); ++i)
+    if (TREE_CODE (*i) == CHECK_CONSTR)
+      {
+        if (bool* b = lookup_subsumption_result (*i, t))
+          return *b ? valid : invalid;
+      }
+  return undecided;
+}
+
+/* Determine if the terms in TS provide sufficient support for proving
+   the proposition T. If any term in TS is a concept check that is known
+   to subsume T, then the proof is valid. Otherwise, we have to expand T
+   and continue searching for support.  */
+
+proof_result
+analyze_check (term_list& ts, tree t)
+{
+  proof_result r = search_known_subsumptions (ts, t);
+  if (r != undecided)
+    return r;
+
+  tree tmpl = CHECK_CONSTR_CONCEPT (t);
+  tree args = CHECK_CONSTR_ARGS (t);
+  tree c = expand_concept (tmpl, args);
+  return check_term (ts, c);
+}
+
+/* Recursively check constraints of the parameterized constraint. */
+
+proof_result
+analyze_parameterized (term_list& ts, tree t)
+{
+  return check_term (ts, PARM_CONSTR_OPERAND (t));
+}
+
+proof_result
+analyze_conjunction (term_list& ts, tree t)
+{
+  proof_result r = check_term (ts, TREE_OPERAND (t, 0));
+  if (r == invalid || r == undecided)
+    return r;
+  return check_term (ts, TREE_OPERAND (t, 1));
+}
+
+proof_result
+analyze_disjunction (term_list& ts, tree t)
+{
+  proof_result r = check_term (ts, TREE_OPERAND (t, 0));
+  if (r == valid)
+    return r;
+  return check_term (ts, TREE_OPERAND (t, 1));
+}
+
+proof_result
+analyze_term (term_list& ts, tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CHECK_CONSTR:
+      return analyze_check (ts, t);
+
+    case PARM_CONSTR:
+      return analyze_parameterized (ts, t);
+
+    case CONJ_CONSTR:
+      return analyze_conjunction (ts, t);
+    case DISJ_CONSTR:
+      return analyze_disjunction (ts, t);
+
+    case PRED_CONSTR:
+    case EXPR_CONSTR:
+    case TYPE_CONSTR:
+    case ICONV_CONSTR:
+    case DEDUCT_CONSTR:
+    case EXCEPT_CONSTR:
+      return analyze_atom (ts, t);
+
+    case EXPR_PACK_EXPANSION:
+      return analyze_pack (ts, t);
+
+    case ERROR_MARK:
+      /* Encountering an error anywhere in a constraint invalidates
+         the proof, since the constraint is ill-formed.  */
+      return invalid;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Check if a single term can be proven from a set of assumptions.
+   If the proof is not valid, then it is incomplete when either
+   the given term is non-atomic or any term in the list of assumptions
+   is not-atomic.  */
+
+proof_result
+check_term (term_list& ts, tree t)
+{
+  /* Try the easy way; search for an equivalent term.  */
+  if (ts.includes (t))
+    return valid;
+
+  /* The hard way; actually consider what the term means.  */
+  return analyze_term (ts, t);
+}
+
+/* Check to see if any term is proven by the assumptions in the
+   proof goal. The proof is valid if the proof of any term is valid.
+   If validity cannot be determined, but any particular
+   check was undecided, then this goal is undecided.  */
+
+proof_result
+check_goal (proof_goal& g)
+{
+  term_list::iterator iter = g.conclusions.begin ();
+  term_list::iterator end = g.conclusions.end ();
+  bool incomplete = false;
+  while (iter != end)
+    {
+      proof_result r = check_term (g.assumptions, *iter);
+      if (r == valid)
+        return r;
+      if (r == undecided)
+        incomplete = true;
+      ++iter;
+    }
+
+    /* Was the proof complete? */
+    if (incomplete)
+      return undecided;
+    else
+      return invalid;
+}
+
+/* Check if the the proof is valid. This is the case when all
+   goals can be discharged. If any goal is invalid, then the
+   entire proof is invalid. Otherwise, the proof is undecided.  */
+
+proof_result
+check_proof (proof_state& p)
+{
+  proof_state::iterator iter = p.begin();
+  proof_state::iterator end = p.end();
+  while (iter != end)
+    {
+      proof_result r = check_goal (*iter);
+      if (r == invalid)
+        return r;
+      if (r == valid)
+        iter = p.discharge (iter);
+      else
+        ++iter;
+    }
+
+  /* If all goals are discharged, then the proof is valid.  */
+  if (p.empty())
+    return valid;
+  else
+    return undecided;
+}
+
+/*---------------------------------------------------------------------------
+                           Left logical rules
+---------------------------------------------------------------------------*/
+
+term_list::iterator
+load_check_assumption (term_list& ts, term_list::iterator i)
+{
+  tree decl = CHECK_CONSTR_CONCEPT (*i);
+  tree tmpl = DECL_TI_TEMPLATE (decl);
+  tree args = CHECK_CONSTR_ARGS (*i);
+  return ts.replace(i, expand_concept (tmpl, args));
+}
+
+term_list::iterator
+load_parameterized_assumption (term_list& ts, term_list::iterator i)
+{
+  return ts.replace(i, PARM_CONSTR_OPERAND(*i));
+}
+
+term_list::iterator
+load_conjunction_assumption (term_list& ts, term_list::iterator i)
+{
+  tree t1 = TREE_OPERAND (*i, 0);
+  tree t2 = TREE_OPERAND (*i, 1);
+  return ts.replace(i, t1, t2);
+}
+
+/* Examine the terms in the list, and apply left-logical rules to move
+   terms into the set of assumptions. */
+
+void
+load_assumptions (proof_goal& g)
+{
+  term_list::iterator iter = g.assumptions.begin();
+  term_list::iterator end = g.assumptions.end();
+  while (iter != end)
+    {
+      switch (TREE_CODE (*iter))
+        {
+        case CHECK_CONSTR:
+          iter = load_check_assumption (g.assumptions, iter);
+          break;
+        case PARM_CONSTR:
+          iter = load_parameterized_assumption (g.assumptions, iter);
+          break;
+        case CONJ_CONSTR:
+          iter = load_conjunction_assumption (g.assumptions, iter);
+          break;
+        default:
+          ++iter;
+          break;
+        }
+    }
+}
+
+/* In each subgoal, load constraints into the assumption set.  */
+
+void
+load_assumptions(proof_state& p)
+{
+  proof_state::iterator iter = p.begin();
+  while (iter != p.end())
+    {
+      load_assumptions (*iter);
+      ++iter;
+    }
+}
+
+void
+explode_disjunction (proof_state& p, proof_state::iterator gi, term_list::iterator ti1)
+{
+  tree t1 = TREE_OPERAND (*ti1, 0);
+  tree t2 = TREE_OPERAND (*ti1, 1);
+
+  /* Erase the current term from the goal. */
+  proof_goal& g1 = *gi;
+  proof_goal& g2 = *p.branch (gi);
+
+  /* Get an iterator to the equivalent position in th enew goal. */
+  int n = std::distance (g1.assumptions.begin (), ti1);
+  term_list::iterator ti2 = g2.assumptions.begin ();
+  std::advance (ti2, n);
+
+  /* Replace the disjunction in both branches. */
+  g1.assumptions.replace (ti1, t1);
+  g2.assumptions.replace (ti2, t2);
+}
+
+
+/* Search the assumptions of the goal for the first disjunction. */
+
+bool
+explode_goal (proof_state& p, proof_state::iterator gi)
+{
+  term_list& ts = gi->assumptions;
+  term_list::iterator ti = ts.begin();
+  term_list::iterator end = ts.end();
+  while (ti != end)
+    {
+      if (TREE_CODE (*ti) == DISJ_CONSTR)
+        {
+          explode_disjunction (p, gi, ti);
+          return true;
+        }
+      else ++ti;
+    }
+  return false;
+}
+
+/* Search for the first goal with a disjunction, and then branch
+   creating a clone of that subgoal. */
+
+void
+explode_assumptions (proof_state& p)
+{
+  proof_state::iterator iter = p.begin();
+  proof_state::iterator end = p.end();
+  while (iter != end)
+    {
+      if (explode_goal (p, iter))
+        return;
+      ++iter;
+    }
+}
+
+
+/*---------------------------------------------------------------------------
+                           Right logical rules
+---------------------------------------------------------------------------*/
+
+term_list::iterator
+load_disjunction_conclusion (term_list& g, term_list::iterator i)
+{
+  tree t1 = TREE_OPERAND (*i, 0);
+  tree t2 = TREE_OPERAND (*i, 1);
+  return g.replace(i, t1, t2);
+}
+
+/* Apply logical rules to the right hand side. This will load the
+   conclusion set with all tpp-level disjunctions.  */
+
+void
+load_conclusions (proof_goal& g)
+{
+  term_list::iterator iter = g.conclusions.begin();
+  term_list::iterator end = g.conclusions.end();
+  while (iter != end)
+    {
+      if (TREE_CODE (*iter) == DISJ_CONSTR)
+        iter = load_disjunction_conclusion (g.conclusions, iter);
+      else
+        ++iter;
+    }
+}
+
+void
+load_conclusions (proof_state& p)
+{
+  proof_state::iterator iter = p.begin();
+  while (iter != p.end())
+    {
+      load_conclusions (*iter);
+      ++iter;
+    }
+}
+
+
+/*---------------------------------------------------------------------------
+                          High-level proof tactics
+---------------------------------------------------------------------------*/
+
+/* Given two constraints A and C, try to derive a proof that
+   A implies C.  */
+
+bool
+prove_implication (tree a, tree c)
+{
+  /* Quick accept. */
+  if (cp_tree_equal (a, c))
+    return true;
+
+  /* Build the initial proof state. */
+  proof_state proof;
+  proof_goal& goal = proof.front();
+  goal.assumptions.push_back(a);
+  goal.conclusions.push_back(c);
+
+  /* Perform an initial right-expansion in the off-chance that the right
+     hand side contains disjunctions. */
+  load_conclusions (proof);
+
+  int step_max = 1 << 10;
+  int step_count = 0;              /* FIXME: We shouldn't have this. */
+  std::size_t branch_limit = 1024; /* FIXME: This needs to be configurable. */
+  while (step_count < step_max && proof.size() < branch_limit)
+    {
+      /* Determine if we can prove that the assumptions entail the
+         conclusions. If so, we're done. */
+      load_assumptions (proof);
+
+      /* Can we solve the proof based on this? */
+      proof_result r = check_proof (proof);
+      if (r != undecided)
+        return r == valid;
+
+      /* If not, then we need to dig into disjunctions.  */
+      explode_assumptions (proof);
+
+      ++step_count;
+    }
+
+  if (step_count == step_max)
+    error ("subsumption failed to resolve");
+
+  if (proof.size() == branch_limit)
+    error ("exceeded maximum number of branches");
+
+  return false;
+}
+
+/* Returns true if the LEFT constraint subsume the RIGHT constraints.
+   This is done by deriving a proof of the conclusions on the RIGHT
+   from the assumptions on the LEFT assumptions.  */
+
+bool
+subsumes_constraints_nonnull (tree left, tree right)
+{
+  gcc_assert (check_constraint_info (left));
+  gcc_assert (check_constraint_info (right));
+
+  auto_timevar time (TV_CONSTRAINT_SUB);
+  tree a = CI_ASSOCIATED_CONSTRAINTS (left);
+  tree c = CI_ASSOCIATED_CONSTRAINTS (right);
+  return prove_implication (a, c);
+}
+
+} /* namespace */
+
+/* Returns true if the LEFT constraints subsume the RIGHT
+   constraints.  */
+
+bool
+subsumes (tree left, tree right)
+{
+  if (left == right)
+    return true;
+  if (!left)
+    return false;
+  if (!right)
+    return true;
+  return subsumes_constraints_nonnull (left, right);
+}