/* * Copyright (C) 2015 Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * Copyright (C) 2015 University of Freiburg * * This file is part of the ULTIMATE ModelCheckerUtils Library. * * The ULTIMATE ModelCheckerUtils Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE ModelCheckerUtils Library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ULTIMATE ModelCheckerUtils Library. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE ModelCheckerUtils Library, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE ModelCheckerUtils Library grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.hoaretriple; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import de.uni_freiburg.informatik.ultimate.core.model.translation.IProgramExecution.ProgramState; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.CfgSmtToolkit; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.ModifiableGlobalsTable; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.OldVarsAssignmentCache; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IAction; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.ICallAction; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IInternalAction; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IReturnAction; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.transitions.TransFormula; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.transitions.TransFormulaUtils; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.transitions.UnmodifiableTransFormula; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.GlobalProgramVar; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.IProgramNonOldVar; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.IProgramOldVar; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.IProgramVar; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.ProgramVarUtils; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.IPredicate; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.IncrementalPlicationChecker; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.IncrementalPlicationChecker.Validity; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.ManagedScript; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SmtUtils; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SubTermFinder; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.Substitution; import de.uni_freiburg.informatik.ultimate.logic.Annotation; import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm; import de.uni_freiburg.informatik.ultimate.logic.FormulaUnLet; import de.uni_freiburg.informatik.ultimate.logic.QuotedObject; import de.uni_freiburg.informatik.ultimate.logic.Script.LBool; import de.uni_freiburg.informatik.ultimate.logic.Sort; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.TermVariable; import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedHashMap; public class IncrementalHoareTripleChecker implements IHoareTripleChecker { private static final String MSG_UNEXPECTED_QUICKCHECK_RESULT = "unexpected quickcheck result"; private static final String MSG_WRONG_KIND_OF_ACTION = "Wrong kind of action"; private static final String ANNOT_NAMED = ":named"; public static final boolean UNLET_TERMS = true; protected static final String ID_PRECONDITION = "precondition"; protected static final String ID_PRECONDITION_NON_MOD_GLOBAL_EQUALITY = "precondNonModGlobalEquality"; protected static final String ID_TRANSITION_MODIFIABLE_GLOBAL_EQUALITY = "modifiableVarEquality"; protected static final String ID_TRANSITION_FORMULA = "codeBlock"; protected static final String ID_LOCAL_VARS_ASSIGNMENT = "localVarsAssignment"; protected static final String ID_HIERACHICAL_PRECONDITION = "hierarchicalPrecondition"; protected static final String ID_NEGATED_POSTCONDITION = "negatedPostcondition"; private static final String MSG_START_EDGE_CHECK = "starting to check validity of Hoare triples"; private static final String MSG_END_EDGE_CHECK = "finished to check validity of Hoare triples"; protected final ManagedScript mManagedScript; protected final ModifiableGlobalsTable mModifiableGlobalVariableManager; private final OldVarsAssignmentCache mOldVarsAssignmentCache; private IPredicate mAssertedPrecond; protected IPredicate mAssertedHier; protected IAction mAssertedAction; private IPredicate mAssertedPostcond; private ScopedHashMap mHierConstants; public final boolean mUseNamedTerms = true; protected final HoareTripleCheckerStatisticsGenerator mEdgeCheckerBenchmark; private final boolean mConstructCounterexamples; private ProgramState mCounterexampleStatePrecond; private ProgramState mCounterexampleStatePostcond; /** * @param csToolkit * A {@link CfgSmtToolkit} instance. * @param constructCounterexamples * if a Hoare triple was invalid, then construct a pair of {@link ProgramState}s. This pair constitutes a * counterexample to validity. One {@link ProgramState} represents a state before executing the * transition, the other represents a state after executing the transition. * * TODO: Return a third {@link ProgramState} in counterexamples to validity of return transitions (will * represent state before call) */ public IncrementalHoareTripleChecker(final CfgSmtToolkit csToolkit, final boolean constructCounterexamples) { mManagedScript = csToolkit.getManagedScript(); mModifiableGlobalVariableManager = csToolkit.getModifiableGlobalsTable(); mOldVarsAssignmentCache = csToolkit.getOldVarsAssignmentCache(); mEdgeCheckerBenchmark = new HoareTripleCheckerStatisticsGenerator(); mConstructCounterexamples = constructCounterexamples; } @Override public HoareTripleCheckerStatisticsGenerator getStatistics() { return mEdgeCheckerBenchmark; } @Override public Validity checkInternal(final IPredicate pre, final IInternalAction act, final IPredicate post) { final LBool quickCheckTrans = prepareAssertionStackAndAddTransition(act); if (quickCheckTrans == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckLinPred = prepareAssertionStackAndAddPrecondition(pre); if (quickCheckLinPred == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckPostcond = prepareAssertionStackAndAddPostcond(post); if (quickCheckPostcond == LBool.UNSAT) { return Validity.VALID; } assert quickCheckPostcond == LBool.UNKNOWN || quickCheckPostcond == null : MSG_UNEXPECTED_QUICKCHECK_RESULT; assert mAssertedPrecond == pre && mAssertedHier == null && mAssertedAction == act && mAssertedPostcond == post; return checkValidity(); } @Override public Validity checkCall(final IPredicate pre, final ICallAction act, final IPredicate post) { final LBool quickCheckTrans = prepareAssertionStackAndAddTransition(act); if (quickCheckTrans == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckLinPred = prepareAssertionStackAndAddPrecondition(pre); if (quickCheckLinPred == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckPostcond = prepareAssertionStackAndAddPostcond(post); if (quickCheckPostcond == LBool.UNSAT) { return Validity.VALID; } assert quickCheckPostcond == LBool.UNKNOWN || quickCheckPostcond == null : MSG_UNEXPECTED_QUICKCHECK_RESULT; assert mAssertedPrecond == pre && mAssertedHier == null && mAssertedAction == act && mAssertedPostcond == post; return checkValidity(); } @Override public Validity checkReturn(final IPredicate linPre, final IPredicate hierPre, final IReturnAction act, final IPredicate postcond) { final LBool quickCheckTrans = prepareAssertionStackAndAddTransition(act); if (quickCheckTrans == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckLinPred = prepareAssertionStackAndAddPrecondition(linPre); if (quickCheckLinPred == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckHierPred = prepareAssertionStackAndAddHierpred(hierPre); if (quickCheckHierPred == LBool.UNSAT) { return Validity.VALID; } final LBool quickCheckPostcond = prepareAssertionStackAndAddPostcond(postcond); if (quickCheckPostcond == LBool.UNSAT) { return Validity.VALID; } assert quickCheckPostcond == LBool.UNKNOWN || quickCheckPostcond == null : MSG_UNEXPECTED_QUICKCHECK_RESULT; assert mAssertedPrecond == linPre && mAssertedHier == hierPre && mAssertedAction == act && mAssertedPostcond == postcond; return checkValidity(); } protected LBool prepareAssertionStackAndAddTransition(final IAction act) { if (mAssertedAction != act) { if (mAssertedAction != null) { if (mAssertedPrecond != null) { if (mAssertedPostcond != null) { unAssertPostcondition(); } if (mAssertedHier != null) { unAssertHierPred(); } unAssertPrecondition(); } unAssertCodeBlock(); } final LBool quickCheck = assertCodeBlock(act); return quickCheck; } return null; } protected LBool prepareAssertionStackAndAddPrecondition(final IPredicate precond) { if (mAssertedPrecond != precond) { if (mAssertedPrecond != null) { if (mAssertedPostcond != null) { unAssertPostcondition(); } if (mAssertedHier != null) { unAssertHierPred(); } unAssertPrecondition(); } final LBool quickCheck = assertPrecondition(precond); return quickCheck; } return null; } protected LBool prepareAssertionStackAndAddHierpred(final IPredicate hierpred) { if (mAssertedHier != hierpred) { if (mAssertedPostcond != null) { unAssertPostcondition(); } if (mAssertedHier != null) { unAssertHierPred(); } final LBool quickCheck = assertHierPred(hierpred); return quickCheck; } return null; } protected LBool prepareAssertionStackAndAddPostcond(final IPredicate postcond) { if (mAssertedPostcond != postcond) { if (mAssertedPostcond != null) { unAssertPostcondition(); } final LBool quickCheck = assertPostcond(postcond); return quickCheck; } return null; } protected LBool assertPostcond(final IPredicate postcond) { if (mAssertedAction instanceof IInternalAction) { return assertPostcondInternal(postcond); } if (mAssertedAction instanceof ICallAction) { return assertPostcondCall(postcond); } if (mAssertedAction instanceof IReturnAction) { return assertPostcondReturn(postcond); } throw new AssertionError("unknown trans type"); } public void clearAssertionStack() { if (mAssertedPostcond != null) { unAssertPostcondition(); } if (mAssertedPrecond != null) { unAssertPrecondition(); } if (mAssertedHier != null) { unAssertHierPred(); } if (mAssertedAction != null) { unAssertCodeBlock(); } } @Override public void releaseLock() { clearAssertionStack(); assert !mManagedScript.isLocked() : "script should not be locked"; } private LBool assertPrecondition(final IPredicate p) { assert mManagedScript.isLockOwner(this); assert mAssertedAction != null : "Assert CodeBlock first"; assert mAssertedPrecond == null : "precond already asserted"; mAssertedPrecond = p; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); mManagedScript.push(this, 1); Term predcondition = p.getClosedFormula(); if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_PRECONDITION); predcondition = mManagedScript.annotate(this, predcondition, annot); } LBool quickCheck = mManagedScript.assertTerm(this, predcondition); final String predProc = mAssertedAction.getPrecedingProcedure(); final Set modifiableGlobals = mModifiableGlobalVariableManager.getModifiedBoogieVars(predProc); final Collection oldVarEqualities = constructNonModOldVarsEquality(p.getVars(), modifiableGlobals, mManagedScript, this); if (!oldVarEqualities.isEmpty()) { Term nonModOldVarsEquality = SmtUtils.and(mManagedScript.getScript(), oldVarEqualities.toArray(new Term[oldVarEqualities.size()])); if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_PRECONDITION_NON_MOD_GLOBAL_EQUALITY); nonModOldVarsEquality = mManagedScript.annotate(this, nonModOldVarsEquality, annot); } quickCheck = mManagedScript.assertTerm(this, nonModOldVarsEquality); } mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return quickCheck; } /** * Return a set of equalities such that for each oldvar old(g) that occurs in vars that is not contained in * oldVarsOfModifiableGlobals there is an equality (= c_g c_old(g)) where c_g is the default constant of the global * variable g and c_old(g) is the default constant of old(g). */ protected static Collection constructNonModOldVarsEquality(final Set vars, final Set oldVarsOfModifiableGlobals, final ManagedScript mgdScript, final Object lock) { final Collection conjunction = new ArrayList<>(); for (final IProgramVar bv : vars) { if (bv instanceof IProgramOldVar) { final IProgramNonOldVar pnov = ((IProgramOldVar) bv).getNonOldVar(); if (!oldVarsOfModifiableGlobals.contains(pnov)) { conjunction.add(oldVarsEquality((IProgramOldVar) bv, mgdScript, lock)); } } } return conjunction; } static private Term oldVarsEquality(final IProgramOldVar oldVar, final ManagedScript mgdScript, final Object lock) { assert oldVar.isOldvar(); final IProgramVar nonOldVar = oldVar.getNonOldVar(); final Term equality = mgdScript.term(lock, "=", oldVar.getDefaultConstant(), nonOldVar.getDefaultConstant()); return equality; } private void unAssertPrecondition() { assert mManagedScript.isLockOwner(this); assert mAssertedPrecond != null : "No PrePred asserted"; mAssertedPrecond = null; mManagedScript.pop(this, 1); if (mAssertedAction == null) { throw new AssertionError("CodeBlock is assigned first"); } } protected LBool assertCodeBlock(final IAction act) { if (mManagedScript.isLocked()) { mManagedScript.requestLockRelease(); } mManagedScript.lock(this); mManagedScript.echo(this, new QuotedObject(MSG_START_EDGE_CHECK)); assert mAssertedAction == null : "CodeBlock already asserted"; mAssertedAction = act; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); mManagedScript.push(this, 1); Term cbFormula; if (act instanceof IInternalAction) { cbFormula = ((IInternalAction) act).getTransformula().getClosedFormula(); } else if (act instanceof ICallAction) { cbFormula = ((ICallAction) act).getLocalVarsAssignment().getClosedFormula(); } else if (act instanceof IReturnAction) { cbFormula = ((IReturnAction) act).getAssignmentOfReturn().getClosedFormula(); } else { throw new AssertionError("unknown action"); } if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_TRANSITION_FORMULA); cbFormula = mManagedScript.annotate(this, cbFormula, annot); } LBool quickCheck = mManagedScript.assertTerm(this, cbFormula); if (act instanceof IReturnAction) { mHierConstants = new ScopedHashMap<>(); final IReturnAction ret = (IReturnAction) act; final String proc = ret.getPrecedingProcedure(); final UnmodifiableTransFormula ovaTF = mOldVarsAssignmentCache.getOldVarsAssignment(proc); Term ovaFormula = ovaTF.getFormula(); // rename modifiable globals to Hier vars ovaFormula = renameVarsToHierConstants(ovaTF.getInVars(), ovaFormula); // rename oldVars of modifiable globals to default vars ovaFormula = renameVarsToDefaultConstants(ovaTF.getOutVars(), ovaFormula); if (UNLET_TERMS) { ovaFormula = new FormulaUnLet().unlet(ovaFormula); } assert ovaFormula.getFreeVars().length == 0; if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_TRANSITION_MODIFIABLE_GLOBAL_EQUALITY); ovaFormula = mManagedScript.annotate(this, ovaFormula, annot); } quickCheck = mManagedScript.assertTerm(this, ovaFormula); final Set modifiableGlobals = ovaTF.getInVars().keySet(); final UnmodifiableTransFormula callTf = ret.getLocalVarsAssignmentOfCall(); Term locVarAssign = callTf.getFormula(); // TODO: rename non-modifiable globals to DefaultConstants locVarAssign = renameNonModifiableGlobalsToDefaultConstants(callTf.getInVars(), modifiableGlobals, locVarAssign); // rename arguments vars to Hier vars locVarAssign = renameVarsToHierConstants(callTf.getInVars(), locVarAssign); // rename proc parameter vars to DefaultConstants locVarAssign = renameVarsToDefaultConstants(callTf.getOutVars(), locVarAssign); // rename auxiliary vars to fresh constants locVarAssign = renameAuxVarsToCorrespondingConstants(callTf.getAuxVars(), locVarAssign); if (UNLET_TERMS) { locVarAssign = new FormulaUnLet().unlet(locVarAssign); } assert locVarAssign.getFreeVars().length == 0; if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_LOCAL_VARS_ASSIGNMENT); locVarAssign = mManagedScript.annotate(this, locVarAssign, annot); } quickCheck = mManagedScript.assertTerm(this, locVarAssign); } mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return quickCheck; } protected void unAssertCodeBlock() { assert mManagedScript.isLockOwner(this); assert mAssertedAction != null : "No CodeBlock asserted"; mAssertedAction = null; mHierConstants = null; mManagedScript.pop(this, 1); if (mAssertedPrecond != null) { throw new AssertionError("CodeBlock is unasserted last"); } mManagedScript.echo(this, new QuotedObject(MSG_END_EDGE_CHECK)); mManagedScript.unlock(this); } protected LBool assertHierPred(final IPredicate p) { assert mManagedScript.isLockOwner(this); assert mAssertedAction != null : "assert Return first"; assert mAssertedAction instanceof IReturnAction : "assert Return first"; assert mAssertedPrecond != null : "assert precond fist"; assert mAssertedHier == null : "HierPred already asserted"; mAssertedHier = p; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); mManagedScript.push(this, 1); mHierConstants.beginScope(); Term hierFormula = p.getFormula(); // rename globals that are not modifiable by callee to default constants final String callee = mAssertedAction.getPrecedingProcedure(); final Set modifiableGlobalsCallee = mModifiableGlobalVariableManager.getModifiedBoogieVars(callee); hierFormula = renameNonModifiableNonOldGlobalsToDefaultConstants(p.getVars(), modifiableGlobalsCallee, hierFormula); // rename oldvars of globals that are not modifiable by caller to // default constants of nonOldVar final String caller = mAssertedAction.getSucceedingProcedure(); final Set modifiableGlobalsCaller = mModifiableGlobalVariableManager.getModifiedBoogieVars(caller); hierFormula = renameNonModifiableOldGlobalsToDefaultConstantOfNonOldVar(p.getVars(), modifiableGlobalsCaller, hierFormula, mManagedScript, this); // rename vars which are assigned on return to Hier vars hierFormula = renameVarsToHierConstants(p.getVars(), hierFormula); if (UNLET_TERMS) { hierFormula = new FormulaUnLet().unlet(hierFormula); } // TODO auxvars assert hierFormula.getFreeVars().length == 0; if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_HIERACHICAL_PRECONDITION); hierFormula = mManagedScript.annotate(this, hierFormula, annot); } final LBool quickCheck = mManagedScript.assertTerm(this, hierFormula); mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return quickCheck; } /** * Return a set of equalities such that for each oldvar old(g) that occurs in vars and for which the corresponding * nonOldVar occurs in modifiableGlobalsCaller but not in modifiableGlobalsCallee we add the equality (= c_old(g) * c_g_hier) and for each nonOldVar that occurs in modifiableGlobalsCaller but not in modifiableGlobalsCallee we add * the equality (= c_g c_g_hier), where c_g is the default constant of the global variable g and c_old(g) is the * default constant of old(g) and c_g_hier is the constant for the nonOldVar g at the position of the hierarchical * predecessor. */ private Collection constructCalleeNonModOldVarsEquality(final Set vars, final Set modifiableGlobalsCaller, final Set modifiableGlobalsCallee) { if (!modifiableGlobalsCallee.containsAll(modifiableGlobalsCaller)) { final boolean test = true; } final Collection conjunction = new ArrayList<>(); for (final IProgramVar bv : vars) { if (bv instanceof GlobalProgramVar) { IProgramNonOldVar bnov; if (bv instanceof IProgramOldVar) { bnov = ((IProgramOldVar) bv).getNonOldVar(); } else { bnov = (IProgramNonOldVar) bv; } if (modifiableGlobalsCaller.contains(bnov) && !modifiableGlobalsCallee.contains(bnov)) { final Term hierConst = getOrConstructHierConstant(bnov); final Term conjunct = SmtUtils.binaryEquality(mManagedScript.getScript(), bv.getDefaultConstant(), hierConst); conjunction.add(conjunct); } } } return conjunction; } protected void unAssertHierPred() { assert mManagedScript.isLockOwner(this); assert mAssertedHier != null : "No HierPred asserted"; assert mAssertedAction instanceof IReturnAction : MSG_WRONG_KIND_OF_ACTION; mAssertedHier = null; mManagedScript.pop(this, 1); mHierConstants.endScope(); } private LBool assertPostcondInternal(final IPredicate p) { assert mManagedScript.isLockOwner(this); assert mAssertedPrecond != null; assert mAssertedAction != null; assert mAssertedAction instanceof IInternalAction : MSG_WRONG_KIND_OF_ACTION; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); mManagedScript.push(this, 1); mAssertedPostcond = p; // OldVars renamed (depending on modifiability) // All variables get index 0 // assigned vars (locals and globals) get index 1 // other vars get index 0 Term renamedFormula = constructPostcondFormula(p, (IInternalAction) mAssertedAction, mModifiableGlobalVariableManager, mManagedScript, this); if (UNLET_TERMS) { renamedFormula = new FormulaUnLet().unlet(renamedFormula); } assert renamedFormula.getFreeVars().length == 0; Term negation = mManagedScript.term(this, "not", renamedFormula); if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_NEGATED_POSTCONDITION); negation = mManagedScript.annotate(this, negation, annot); } final LBool isSat = mManagedScript.assertTerm(this, negation); mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return isSat; } public static Term constructPostcondFormula(final IPredicate p, final IInternalAction action, final ModifiableGlobalsTable mgt, final ManagedScript mgdScript, final Object lock) { final Set assignedVars = action.getTransformula().getAssignedVars(); Term renamedFormula = renameVarsToPrimedConstants(assignedVars, p.getFormula(), mgdScript, lock); final String succProc = action.getSucceedingProcedure(); final Set modifiableGlobals = mgt.getModifiedBoogieVars(succProc); renamedFormula = renameNonModifiableOldGlobalsToDefaultConstantOfNonOldVar(p.getVars(), modifiableGlobals, renamedFormula, mgdScript, lock); renamedFormula = renameVarsToDefaultConstants(p.getVars(), renamedFormula, mgdScript, lock); return renamedFormula; } private LBool assertPostcondCall(final IPredicate p) { assert mManagedScript.isLockOwner(this); assert mAssertedPrecond != null; assert mAssertedAction != null; assert mAssertedAction instanceof ICallAction : MSG_WRONG_KIND_OF_ACTION; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); mManagedScript.push(this, 1); mAssertedPostcond = p; final Set boogieVars = p.getVars(); // rename oldVars to default constants of non-oldvars Term renamedFormula = renameGlobalsAndOldVarsToNonOldDefaultConstants(boogieVars, p.getFormula()); // rename remaining variables renamedFormula = renameVarsToPrimedConstants(boogieVars, renamedFormula, mManagedScript, this); renamedFormula = renameVarsToDefaultConstants(p.getVars(), renamedFormula, mManagedScript, this); if (UNLET_TERMS) { renamedFormula = new FormulaUnLet().unlet(renamedFormula); } assert renamedFormula.getFreeVars().length == 0; Term negation = mManagedScript.term(this, "not", renamedFormula); if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_NEGATED_POSTCONDITION); negation = mManagedScript.annotate(this, negation, annot); } final LBool isSat = mManagedScript.assertTerm(this, negation); mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return isSat; } private LBool assertPostcondReturn(final IPredicate p) { assert mManagedScript.isLockOwner(this); assert mAssertedPrecond != null; assert mAssertedAction instanceof IReturnAction : MSG_WRONG_KIND_OF_ACTION; assert mAssertedHier != null; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); mManagedScript.push(this, 1); mHierConstants.beginScope(); mAssertedPostcond = p; // rename the variables that are assigned by the return to primed vars final Set assignedVars = ((IReturnAction) mAssertedAction).getAssignmentOfReturn().getOutVars().keySet(); Term renamedFormula = renameVarsToPrimedConstants(assignedVars, p.getFormula(), mManagedScript, this); final String callee = mAssertedAction.getPrecedingProcedure(); final Set modifiableGlobalsCallee = mModifiableGlobalVariableManager.getModifiedBoogieVars(callee); // rename modifiable globals to default constants renamedFormula = renameVarsToDefaultConstants(modifiableGlobalsCallee, renamedFormula, mManagedScript, this); // rename globals that are not modifiable by callee to default constants renamedFormula = renameNonModifiableNonOldGlobalsToDefaultConstants(p.getVars(), modifiableGlobalsCallee, renamedFormula); // rename oldvars of globals that are not modifiable by caller to // default constants of nonOldVar final String caller = mAssertedAction.getSucceedingProcedure(); final Set modifiableGlobalsCaller = mModifiableGlobalVariableManager.getModifiedBoogieVars(caller); renamedFormula = renameNonModifiableOldGlobalsToDefaultConstantOfNonOldVar(p.getVars(), modifiableGlobalsCaller, renamedFormula, mManagedScript, this); // rename remaining non-old Globals to default constants // renamedFormula = renameNonOldGlobalsToDefaultConstants(p.getVars(), renamedFormula); // rename remaining vars to Hier vars renamedFormula = renameVarsToHierConstants(p.getVars(), renamedFormula); if (UNLET_TERMS) { renamedFormula = new FormulaUnLet().unlet(renamedFormula); } assert renamedFormula.getFreeVars().length == 0; Term negation = mManagedScript.term(this, "not", renamedFormula); if (mUseNamedTerms) { final Annotation annot = new Annotation(ANNOT_NAMED, ID_NEGATED_POSTCONDITION); negation = mManagedScript.annotate(this, negation, annot); } final LBool isSat = mManagedScript.assertTerm(this, negation); mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return isSat; } protected void unAssertPostcondition() { assert mManagedScript.isLockOwner(this); assert mAssertedAction != null : "Assert CodeBlock first!"; assert mAssertedPrecond != null : "Assert precond first!"; assert mAssertedPostcond != null : "Assert postcond first!"; mAssertedPostcond = null; mCounterexampleStatePrecond = null; mCounterexampleStatePostcond = null; mManagedScript.pop(this, 1); if (mAssertedAction instanceof IReturnAction) { assert mHierConstants != null : "Assert hierPred first!"; assert mAssertedHier != null : "Assert hierPred first!"; mHierConstants.endScope(); } } protected Validity checkValidity() { assert mManagedScript.isLockOwner(this); assert mAssertedAction != null : "Assert CodeBlock first!"; assert mAssertedPrecond != null : "Assert precond first! "; assert mAssertedPostcond != null : "Assert postcond first! "; mEdgeCheckerBenchmark.continueEdgeCheckerTime(); final LBool isSat = mManagedScript.checkSat(this); switch (isSat) { case SAT: if (mConstructCounterexamples) { mCounterexampleStatePrecond = constructCounterexampleStateForPrecondition(); mCounterexampleStatePostcond = constructCounterexampleStateForPostcondition(); } mEdgeCheckerBenchmark.getSolverCounterSat().incRe(); break; case UNKNOWN: mEdgeCheckerBenchmark.getSolverCounterUnknown().incRe(); break; case UNSAT: mEdgeCheckerBenchmark.getSolverCounterUnsat().incRe(); break; default: throw new AssertionError("unknown case"); } mEdgeCheckerBenchmark.stopEdgeCheckerTime(); return IncrementalPlicationChecker.convertLBool2Validity(isSat); } private ProgramState constructCounterexampleStateForPrecondition() { final UnmodifiableTransFormula tf = mAssertedAction.getTransformula(); return constructCounterexampleState(tf.getInVars(), TransFormulaUtils::constructOutvarsToInvarsMap, x -> TransFormulaUtils.renameInvarsToDefaultVars(tf, mManagedScript, x)); } private ProgramState constructCounterexampleStateForPostcondition() { final UnmodifiableTransFormula tf = mAssertedAction.getTransformula(); return constructCounterexampleState(tf.getOutVars(), TransFormulaUtils::constructInvarsToOutvarsMap, x -> TransFormulaUtils.renameOutvarsToDefaultVars(tf, mManagedScript, x)); } private ProgramState constructCounterexampleState(final Map xVars, final Function> toXVarsMap, final Function xVarToDefaultVar) { final Map representativeTerm2ClosedTerm = new HashMap<>(); final UnmodifiableTransFormula tf = mAssertedAction.getTransformula(); for (final Entry entry : xVars.entrySet()) { if (SmtUtils.isSortForWhichWeCanGetValues(entry.getKey().getTermVariable().getSort())) { final Term xVarConst = UnmodifiableTransFormula.computeClosedFormula(xVars.get(entry.getKey()), tf.getInVars(), tf.getOutVars(), Collections.emptySet(), mManagedScript); representativeTerm2ClosedTerm.put(entry.getKey().getTermVariable(), xVarConst); } } final Set inAndOutVars = tf.getInVars().entrySet().stream().map(Entry::getValue).collect(Collectors.toSet()); inAndOutVars.addAll(tf.getOutVars().entrySet().stream().map(Entry::getValue).collect(Collectors.toSet())); final Set selectTerms = SubTermFinder.find(tf.getFormula(), x -> isSuitableArrayReadTerm(x, inAndOutVars), false); for (final Term selectTerm : selectTerms) { final Term selectTermAllOut = Substitution.apply(mManagedScript, toXVarsMap.apply(tf), selectTerm); final Term selectTermWithDefaultVars = xVarToDefaultVar.apply(selectTermAllOut); // version of the select term as is was asserted final Term selectTermClosed = UnmodifiableTransFormula.computeClosedFormula(selectTermAllOut, tf.getInVars(), tf.getOutVars(), Collections.emptySet(), mManagedScript); representativeTerm2ClosedTerm.put(selectTermWithDefaultVars, selectTermClosed); } return generateProgramState(representativeTerm2ClosedTerm); } private ProgramState generateProgramState(final Map representativeTerm2ClosedTerm) { final Map> valuesMapping = new HashMap<>(); for (final Entry entry : representativeTerm2ClosedTerm.entrySet()) { final Map values = mManagedScript.getValue(this, new Term[] { entry.getValue() }); final Term value = values.get(entry.getValue()); valuesMapping.put(entry.getKey(), Collections.singletonList(value)); } return new ProgramState<>(valuesMapping, Term.class); } /** * @param varSet * here, these are inVar or outVars of a TransFormula * @return true iff term is an array select term, whose Sort allows us to get values and whose free variables are in * the set varSet. */ private boolean isSuitableArrayReadTerm(final Term term, final Set varSet) { return term instanceof ApplicationTerm && ((ApplicationTerm) term).getFunction().getName().equals("select") && SmtUtils.isSortForWhichWeCanGetValues(term.getSort()) && Arrays.stream(term.getFreeVars()).allMatch(x -> varSet.contains(x)); } private static Term renameVarsToDefaultConstants(final Set set, final Term formula, final ManagedScript managedScript, final Object lock) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final IProgramVar bv : set) { replacees.add(bv.getTermVariable()); replacers.add(bv.getDefaultConstant()); } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return managedScript.let(lock, vars, values, formula); } private Term renameVarsToDefaultConstants(final Map bv2tv, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final Entry bv : bv2tv.entrySet()) { replacees.add(bv.getValue()); replacers.add(bv.getKey().getDefaultConstant()); } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } private static Term renameVarsToPrimedConstants(final Set boogieVars, final Term formula, final ManagedScript managedScript, final Object lock) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final IProgramVar bv : boogieVars) { replacees.add(bv.getTermVariable()); replacers.add(bv.getPrimedConstant()); } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return managedScript.let(lock, vars, values, formula); } private Term renameVarsToHierConstants(final Set boogieVars, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final IProgramVar bv : boogieVars) { replacees.add(bv.getTermVariable()); replacers.add(getOrConstructHierConstant(bv)); } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } private Term renameVarsToHierConstants(final Map bv2tv, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final Entry entry : bv2tv.entrySet()) { replacees.add(entry.getValue()); replacers.add(getOrConstructHierConstant(entry.getKey())); } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } private Term renameAuxVarsToCorrespondingConstants(final Set auxVars, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final TermVariable auxVarTv : auxVars) { replacees.add(auxVarTv); final Term correspondingConstant = mManagedScript.term(this, ProgramVarUtils.generateConstantIdentifierForAuxVar(auxVarTv)); replacers.add(correspondingConstant); } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } private Term getOrConstructHierConstant(final IProgramVar bv) { Term preHierConstant = mHierConstants.get(bv); if (preHierConstant == null) { final String name = "c_" + bv.getTermVariable().getName() + "_Hier"; final Sort sort = bv.getTermVariable().getSort(); mManagedScript.declareFun(this, name, new Sort[0], sort); preHierConstant = mManagedScript.term(this, name); mHierConstants.put(bv, preHierConstant); } return preHierConstant; } /** * Rename each g in boogieVars that is not contained in modifiableGlobals to c_g, where c_g is the default constant * for g. */ private Term renameNonModifiableNonOldGlobalsToDefaultConstants(final Set boogieVars, final Set modifiableGlobalsCallee, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final IProgramVar bv : boogieVars) { if (bv.isGlobal() && bv instanceof IProgramNonOldVar && !modifiableGlobalsCallee.contains(bv)) { replacees.add(bv.getTermVariable()); replacers.add(bv.getDefaultConstant()); } } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } /** * Rename oldVars old(g) of non-modifiable globals to the default constants of g. */ private static Term renameNonModifiableOldGlobalsToDefaultConstantOfNonOldVar(final Set boogieVars, final Set modifiableGlobalsCaller, final Term formula, final ManagedScript mgdScript, final Object lock) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final IProgramVar bv : boogieVars) { if (bv instanceof IProgramOldVar) { final IProgramNonOldVar nonOldVar = ((IProgramOldVar) bv).getNonOldVar(); if (modifiableGlobalsCaller.contains(nonOldVar)) { // do nothing } else { replacees.add(bv.getTermVariable()); replacers.add(nonOldVar.getDefaultConstant()); } } } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mgdScript.let(lock, vars, values, formula); } private Term renameNonModifiableGlobalsToDefaultConstants(final Map boogieVars, final Set modifiableGlobals, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final Entry entry : boogieVars.entrySet()) { final IProgramVar bv = entry.getKey(); if (bv.isGlobal()) { if (bv.isOldvar()) { assert !modifiableGlobals.contains(bv); // do nothing } else if (modifiableGlobals.contains(bv)) { // do noting } else { // oldVar of global which is not modifiable by called proc replacees.add(entry.getValue()); replacers.add(bv.getDefaultConstant()); } } else { assert !modifiableGlobals.contains(bv); } } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } private Term renameGlobalsAndOldVarsToNonOldDefaultConstants(final Set boogieVars, final Term formula) { final ArrayList replacees = new ArrayList<>(); final ArrayList replacers = new ArrayList<>(); for (final IProgramVar bv : boogieVars) { if (bv.isGlobal()) { if (bv.isOldvar()) { replacees.add(bv.getTermVariable()); final IProgramVar nonOldbv = ((IProgramOldVar) bv).getNonOldVar(); replacers.add(nonOldbv.getDefaultConstant()); } else { replacees.add(bv.getTermVariable()); replacers.add(bv.getDefaultConstant()); } } } final TermVariable[] vars = replacees.toArray(new TermVariable[replacees.size()]); final Term[] values = replacers.toArray(new Term[replacers.size()]); return mManagedScript.let(this, vars, values, formula); } public boolean isAssertionStackEmpty() { if (mAssertedAction == null) { assert mAssertedPrecond == null; assert mAssertedHier == null; return true; } return false; } public ProgramState getCounterexampleStatePrecond() { if (!mConstructCounterexamples) { throw new IllegalStateException("Construction of counterexamples is not enabled."); } if (mCounterexampleStatePrecond == null) { throw new IllegalStateException("Last response was valid or assertion stack has been altered."); } return mCounterexampleStatePrecond; } public ProgramState getCounterexampleStatePostcond() { if (!mConstructCounterexamples) { throw new IllegalStateException("Construction of counterexamples is not enabled."); } if (mCounterexampleStatePrecond == null) { throw new IllegalStateException("Last response was valid or assertion stack has been altered."); } return mCounterexampleStatePostcond; } }