LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/smt - assertions.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 94 108 87.0 %
Date: 2026-03-28 10:41:09 Functions: 12 13 92.3 %
Branches: 65 90 72.2 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * The module for storing assertions for an SMT engine.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "smt/assertions.h"
      14                 :            : 
      15                 :            : #include <sstream>
      16                 :            : 
      17                 :            : #include "base/modal_exception.h"
      18                 :            : #include "expr/node_algorithm.h"
      19                 :            : #include "expr/subtype_elim_node_converter.h"
      20                 :            : #include "options/base_options.h"
      21                 :            : #include "options/expr_options.h"
      22                 :            : #include "options/language.h"
      23                 :            : #include "options/smt_options.h"
      24                 :            : #include "proof/lazy_proof.h"
      25                 :            : #include "proof/proof_node_algorithm.h"
      26                 :            : #include "smt/env.h"
      27                 :            : #include "theory/trust_substitutions.h"
      28                 :            : #include "util/result.h"
      29                 :            : 
      30                 :            : using namespace cvc5::internal::theory;
      31                 :            : using namespace cvc5::internal::kind;
      32                 :            : 
      33                 :            : namespace cvc5::internal {
      34                 :            : namespace smt {
      35                 :            : 
      36                 :      75752 : Assertions::Assertions(Env& env)
      37                 :            :     : EnvObj(env),
      38                 :      75752 :       d_assertionList(userContext()),
      39                 :      75752 :       d_assertionListDefs(userContext()),
      40                 :     151504 :       d_globalDefineFunLemmasIndex(userContext(), 0)
      41                 :            : {
      42                 :      75752 : }
      43                 :            : 
      44                 :      70732 : Assertions::~Assertions()
      45                 :            : {
      46                 :      70732 : }
      47                 :            : 
      48                 :      67295 : void Assertions::refresh()
      49                 :            : {
      50                 :            :   // Global definitions are asserted now to ensure they always exist. This is
      51                 :            :   // done at the beginning of preprocessing, to ensure that definitions take
      52                 :            :   // priority over, e.g. solving during preprocessing. See issue #7479.
      53                 :      67295 :   size_t numGlobalDefs = d_globalDefineFunLemmas.size();
      54         [ +  + ]:      70093 :   for (size_t i = d_globalDefineFunLemmasIndex.get(); i < numGlobalDefs; i++)
      55                 :            :   {
      56                 :       2798 :     addFormula(d_globalDefineFunLemmas[i], true, false);
      57                 :            :   }
      58                 :      67295 :   d_globalDefineFunLemmasIndex = numGlobalDefs;
      59                 :      67295 : }
      60                 :            : 
      61                 :      50529 : void Assertions::setAssumptions(const std::vector<Node>& assumptions)
      62                 :            : {
      63                 :      50529 :   d_assumptions.clear();
      64                 :      50529 :   d_assumptions = assumptions;
      65                 :            : 
      66         [ +  + ]:      57939 :   for (const Node& n : d_assumptions)
      67                 :            :   {
      68                 :            :     // Ensure expr is type-checked at this point.
      69                 :       7410 :     ensureBoolean(n);
      70                 :       7410 :     addFormula(n, false, false);
      71                 :            :   }
      72                 :      50529 : }
      73                 :            : 
      74                 :     182856 : void Assertions::assertFormula(const Node& n)
      75                 :            : {
      76                 :     182856 :   ensureBoolean(n);
      77                 :     182856 :   bool maybeHasFv = language::isLangSygus(options().base.inputLanguage);
      78                 :     182856 :   addFormula(n, false, maybeHasFv);
      79                 :     182856 : }
      80                 :            : 
      81                 :        250 : std::vector<Node>& Assertions::getAssumptions() { return d_assumptions; }
      82                 :            : 
      83                 :     131048 : const context::CDList<Node>& Assertions::getAssertionList() const
      84                 :            : {
      85                 :     131048 :   return d_assertionList;
      86                 :            : }
      87                 :            : 
      88                 :       6651 : const context::CDList<Node>& Assertions::getAssertionListDefinitions() const
      89                 :            : {
      90                 :       6651 :   return d_assertionListDefs;
      91                 :            : }
      92                 :            : 
      93                 :       2187 : std::unordered_set<Node> Assertions::getCurrentAssertionListDefitions() const
      94                 :            : {
      95                 :       2187 :   std::unordered_set<Node> defSet;
      96         [ +  + ]:       2821 :   for (const Node& a : d_assertionListDefs)
      97                 :            :   {
      98                 :        634 :     defSet.insert(a);
      99                 :            :   }
     100                 :       2187 :   return defSet;
     101                 :          0 : }
     102                 :            : 
     103                 :     205959 : void Assertions::addFormula(TNode n,
     104                 :            :                             bool isFunDef,
     105                 :            :                             bool maybeHasFv)
     106                 :            : {
     107                 :            :   // add to assertion list
     108                 :     205959 :   d_assertionList.push_back(n);
     109 [ +  + ][ +  + ]:     205959 :   if (n.isConst() && n.getConst<bool>())
                 [ +  + ]
     110                 :            :   {
     111                 :            :     // true, nothing to do
     112                 :       5033 :     return;
     113                 :            :   }
     114         [ +  - ]:     401852 :   Trace("smt") << "Assertions::addFormula(" << n
     115                 :     200926 :                << ", isFunDef = " << isFunDef << std::endl;
     116                 :            :   // In non-incremental, we treat higher-order equality as define-fun
     117 [ +  + ][ +  + ]:     200926 :   if (!options().base.incrementalSolving || isFunDef)
                 [ +  + ]
     118                 :            :   {
     119                 :            :     // if a non-recursive define-fun, just add as a top-level substitution
     120 [ +  + ][ +  + ]:     167029 :     if (n.getKind() == Kind::EQUAL && n[0].isVar())
         [ +  + ][ +  + ]
                 [ -  - ]
     121                 :            :     {
     122         [ +  - ]:      45658 :       Trace("smt-define-fun")
     123                 :      22829 :           << "Define fun: " << n[0] << " = " << n[1] << std::endl;
     124                 :      22829 :       NodeManager* nm = nodeManager();
     125                 :      22829 :       TrustSubstitutionMap& tsm = d_env.getTopLevelSubstitutions();
     126                 :      45658 :       if (!isFunDef
     127 [ +  + ][ +  + ]:      57327 :           && (tsm.get().hasSubstitution(n[0])
         [ +  + ][ -  - ]
     128 [ +  + ][ +  + ]:      34498 :               || n[1].getKind() != Kind::LAMBDA))
         [ +  + ][ -  - ]
     129                 :            :       {
     130                 :      11479 :         return;
     131                 :            :       }
     132                 :            :       // If it is a lambda, we rewrite the body, otherwise we rewrite itself.
     133                 :            :       // For lambdas, we prefer rewriting only the body since we don't want
     134                 :            :       // higher-order rewrites (e.g. value normalization) to apply by default.
     135                 :      11350 :       TrustNode defRewBody;
     136                 :            :       // For efficiency, we only do this if it is a lambda.
     137                 :            :       // Note this is important since some benchmarks treat define-fun as a
     138                 :            :       // global let. We should not eagerly rewrite in these cases.
     139         [ +  + ]:      11350 :       if (n[1].getKind() == Kind::LAMBDA)
     140                 :            :       {
     141                 :            :         // Rewrite the body of the lambda.
     142                 :       6554 :         defRewBody = tsm.applyTrusted(n[1][1], d_env.getRewriter());
     143                 :            :       }
     144                 :      11350 :       Node defRew = n[1];
     145                 :            :       // If we rewrote the body
     146         [ +  + ]:      11350 :       if (!defRewBody.isNull())
     147                 :            :       {
     148                 :            :         // The rewritten form is the rewritten body with original variable list.
     149                 :       3265 :         defRew = defRewBody.getNode();
     150                 :       3265 :         defRew = nm->mkNode(Kind::LAMBDA, n[1][0], defRew);
     151                 :            :       }
     152                 :      11350 :       if (!isFunDef && expr::hasSubterm(defRew, n[0]))
     153                 :            :       {
     154                 :         13 :         return;
     155                 :            :       }
     156                 :            :       // if we need to track proofs
     157         [ +  + ]:      11337 :       if (d_env.isProofProducing())
     158                 :            :       {
     159                 :            :         // initialize the proof generator if not already done so
     160         [ +  + ]:       3554 :         if (d_defFunRewPf == nullptr)
     161                 :            :         {
     162                 :        587 :           d_defFunRewPf = std::make_shared<LazyCDProof>(d_env);
     163                 :            :         }
     164                 :            :         // A define-fun is an assumption in the overall proof, thus
     165                 :            :         // we justify the substitution with ASSUME here.
     166                 :       7108 :         d_defFunRewPf->addStep(n, ProofRule::ASSUME, {}, {n});
     167                 :            :         // If changed, prove the rewrite
     168         [ +  + ]:       3554 :         if (defRew != n[1])
     169                 :            :         {
     170                 :       1247 :           Node eqBody = defRewBody.getProven();
     171                 :       1247 :           d_defFunRewPf->addLazyStep(eqBody, defRewBody.getGenerator());
     172                 :       1247 :           Node eqRew = n[1].eqNode(defRew);
     173 [ -  + ][ -  + ]:       1247 :           Assert (n[1].getKind() == Kind::LAMBDA);
                 [ -  - ]
     174                 :            :           // congruence over the binder
     175                 :       1247 :           std::vector<Node> cargs;
     176                 :       1247 :           ProofRule cr = expr::getCongRule(n[1], cargs);
     177                 :       2494 :           d_defFunRewPf->addStep(eqRew, cr, {eqBody}, cargs);
     178                 :            :           // Proof is:
     179                 :            :           //                            ------ from tsm
     180                 :            :           //                            t = t'
     181                 :            :           // ------------------ ASSUME  -------------------------- CONG
     182                 :            :           // n = lambda x. t            lambda x. t = lambda x. t'
     183                 :            :           // ------------------------------------------------------ TRANS
     184                 :            :           // n = lambda x. t'
     185                 :       1247 :           Node eqFinal = n[0].eqNode(defRew);
     186 [ +  + ][ -  - ]:       3741 :           d_defFunRewPf->addStep(eqFinal, ProofRule::TRANS, {n, eqRew}, {});
     187                 :       1247 :         }
     188                 :            :       }
     189         [ +  - ]:      11337 :       Trace("smt-define-fun") << "...rewritten to " << defRew << std::endl;
     190                 :      11337 :       d_assertionListDefs.push_back(n);
     191         [ +  + ]:      22674 :       d_env.getTopLevelSubstitutions().addSubstitution(
     192                 :      11337 :           n[0], defRew, d_defFunRewPf.get());
     193                 :      11337 :       return;
     194                 :      11350 :     }
     195                 :            :   }
     196                 :            : 
     197                 :            :   // Ensure that it does not contain free variables
     198         [ +  + ]:     178097 :   if (maybeHasFv)
     199                 :            :   {
     200                 :            :     // Note that API users and the smt2 parser may generate assertions with
     201                 :            :     // shadowed variables, which are resolved during rewriting. Hence we do not
     202                 :            :     // check for this here.
     203         [ -  + ]:       1767 :     if (expr::hasFreeVar(n))
     204                 :            :     {
     205                 :          0 :       std::stringstream se;
     206         [ -  - ]:          0 :       if (isFunDef)
     207                 :            :       {
     208                 :          0 :         se << "Cannot process function definition with free variable.";
     209                 :            :       }
     210                 :            :       else
     211                 :            :       {
     212                 :          0 :         se << "Cannot process assertion with free variable.";
     213         [ -  - ]:          0 :         if (language::isLangSygus(options().base.inputLanguage))
     214                 :            :         {
     215                 :            :           // Common misuse of SyGuS is to use top-level assert instead of
     216                 :            :           // constraint when defining the synthesis conjecture.
     217                 :          0 :           se << " Perhaps you meant `constraint` instead of `assert`?";
     218                 :            :         }
     219                 :            :       }
     220                 :          0 :       throw ModalException(se.str().c_str());
     221                 :          0 :     }
     222                 :            :   }
     223                 :            : }
     224                 :            : 
     225                 :      14340 : void Assertions::addDefineFunDefinition(Node n, bool global)
     226                 :            : {
     227         [ +  + ]:      14340 :   if (global)
     228                 :            :   {
     229                 :            :     // Global definitions are asserted at check-sat-time because we have to
     230                 :            :     // make sure that they are always present
     231 [ -  + ][ -  + ]:       1445 :     Assert(!language::isLangSygus(options().base.inputLanguage));
                 [ -  - ]
     232                 :       1445 :     d_globalDefineFunLemmas.emplace_back(n);
     233                 :            :   }
     234                 :            :   else
     235                 :            :   {
     236                 :            :     // We don't permit functions-to-synthesize within recursive function
     237                 :            :     // definitions currently. Thus, we should check for free variables if the
     238                 :            :     // input language is SyGuS.
     239                 :      12895 :     bool maybeHasFv = language::isLangSygus(options().base.inputLanguage);
     240                 :      12895 :     addFormula(n, true, maybeHasFv);
     241                 :            :   }
     242                 :      14340 : }
     243                 :            : 
     244                 :     190266 : void Assertions::ensureBoolean(const Node& n)
     245                 :            : {
     246                 :     190266 :   TypeNode type = n.getType(options().expr.typeChecking);
     247         [ -  + ]:     190266 :   if (!type.isBoolean())
     248                 :            :   {
     249                 :          0 :     std::stringstream ss;
     250                 :            :     ss << "Expected Boolean type\n"
     251                 :          0 :        << "The assertion : " << n << "\n"
     252                 :          0 :        << "Its type      : " << type;
     253                 :          0 :     throw TypeCheckingExceptionPrivate(n, ss.str());
     254                 :          0 :   }
     255                 :     190266 : }
     256                 :            : 
     257                 :            : }  // namespace smt
     258                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14