Fix potential for cycles in trust substitutions (#7687)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 24 Nov 2021 05:34:25 +0000 (23:34 -0600)
committerGitHub <noreply@github.com>
Wed, 24 Nov 2021 05:34:25 +0000 (05:34 +0000)
This ensures we use only the prefix of substitutions for the *first* time a formula is proven in a substitution map.

This avoids the possibility for cycles in proof generators during non-clausal simplification, where we may reprove a formula F later at a point where later substitutions depend on F.

src/theory/trust_substitutions.cpp
src/theory/trust_substitutions.h
test/regress/CMakeLists.txt
test/regress/regress1/proofs/str-ovf-dd.smt2 [new file with mode: 0644]

index 7934231b9f9b6ab28811cbd50a8eaa214643a193..c732650959384abe3937c0c1865dbc0bc0cd3ca1 100644 (file)
@@ -162,8 +162,12 @@ TrustNode TrustSubstitutionMap::applyTrusted(Node n, bool doRewrite)
     return TrustNode::mkTrustRewrite(n, ns, nullptr);
   }
   Node eq = n.eqNode(ns);
-  // remember the index
-  d_eqtIndex[eq] = d_tsubs.size();
+  // If we haven't already stored an index, remember the index. Otherwise, a
+  // (possibly shorter) prefix of the substitution already suffices to show eq
+  if (d_eqtIndex.find(eq) == d_eqtIndex.end())
+  {
+    d_eqtIndex[eq] = d_tsubs.size();
+  }
   // this class will provide a proof if asked
   return TrustNode::mkTrustRewrite(n, ns, this);
 }
@@ -194,11 +198,16 @@ std::shared_ptr<ProofNode> TrustSubstitutionMap::getProofFor(Node eq)
   {
     return d_subsPg->getProofFor(eq);
   }
+  Trace("trust-subs-pf") << "getProofFor " << eq << std::endl;
+  AlwaysAssert(d_proving.find(eq) == d_proving.end())
+      << "Repeat getProofFor in TrustSubstitutionMap " << eq;
+  d_proving.insert(eq);
   NodeUIntMap::iterator it = d_eqtIndex.find(eq);
   Assert(it != d_eqtIndex.end());
   Trace("trust-subs-pf") << "TrustSubstitutionMap::getProofFor, # assumptions= "
                          << it->second << std::endl;
   Node cs = getSubstitution(it->second);
+  Trace("trust-subs-pf") << "getProofFor substitution is " << cs << std::endl;
   Assert(eq != cs);
   std::vector<Node> pfChildren;
   if (!cs.isConst())
@@ -252,7 +261,9 @@ std::shared_ptr<ProofNode> TrustSubstitutionMap::getProofFor(Node eq)
   d_applyPg->addSteps(*d_tspb.get());
   d_tspb->clear();
   Trace("trust-subs-pf") << "...finish, make proof" << std::endl;
-  return d_applyPg->getProofFor(eq);
+  std::shared_ptr<ProofNode> ret = d_applyPg->getProofFor(eq);
+  d_proving.erase(eq);
+  return ret;
 }
 
 std::string TrustSubstitutionMap::identify() const { return d_name; }
index 2a6997d1d71d6b73aebf659e1eff82663865ae00..cc08c870d35d0a22afcdc22bab1000bb9fe0b55b 100644 (file)
@@ -143,6 +143,8 @@ class TrustSubstitutionMap : public ProofGenerator
    * two substitutions but not the third when asked to prove this equality.
    */
   NodeUIntMap d_eqtIndex;
+  /** Debugging, catches potential for infinite loops */
+  std::unordered_set<Node> d_proving;
 };
 
 }  // namespace theory
index e11149df0c6106280002fb78edec59db4de1c76d..2ed3ddb8b5220042eea033396db35a83954584d6 100644 (file)
@@ -1847,6 +1847,7 @@ set(regress_1_tests
   regress1/proofs/qgu-fuzz-arrays-1-dd-te-auto.smt2
   regress1/proofs/quant-alpha-eq.smt2
   regress1/proofs/sat-trivial-cycle.smt2
+  regress1/proofs/str-ovf-dd.smt2
   regress1/proofs/unsat-cores-proofs.smt2
   regress1/push-pop/arith_lra_01.smt2
   regress1/push-pop/arith_lra_02.smt2
diff --git a/test/regress/regress1/proofs/str-ovf-dd.smt2 b/test/regress/regress1/proofs/str-ovf-dd.smt2
new file mode 100644 (file)
index 0000000..85a8790
--- /dev/null
@@ -0,0 +1,9 @@
+(set-logic ALL)
+(set-info :status unsat)
+(declare-const x Bool)
+(declare-const a String)
+(declare-const a4 String)
+(assert (= a4 "-"))
+(assert (str.in_re (str.++ a4 a a4) re.allchar))
+(assert (= x (str.in_re a re.allchar)))
+(check-sat)