/* * Copyright (C) 2015 Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * Copyright (C) 2022 Dominik Klumpp (klumpp@informatik.uni-freiburg.de) * Copyright (C) 2015-2022 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 de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.ModifiableGlobalsTable; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfgForkTransitionThreadOther; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfgJoinTransitionThreadOther; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IInternalAction; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.transitions.UnmodifiableTransFormula; 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.smt.predicates.IPredicate; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.IPredicateCoverageChecker; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.IncrementalPlicationChecker.Validity; /** * Implements simple dataflow-based Hoare triple checks for internal actions. * * This class is only meant for internal usage by {@link SdHoareTripleChecker}. * * @author Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * @author Dominik Klumpp (klumpp@informatik.uni-freiburg.de) */ class InternalCheckHelper extends SdHoareTripleCheckHelper { private static final String PRE_HIER_ERROR = "Unexpected hierarchical precondition for internal action"; InternalCheckHelper(final IPredicateCoverageChecker coverage, final IPredicate falsePredicate, final IPredicate truePredicate, final HoareTripleCheckerStatisticsGenerator statistics, final ModifiableGlobalsTable modifiableGlobals) { super(coverage, falsePredicate, truePredicate, statistics, modifiableGlobals); } /** * This method handles 2 cases of Hoare triples where the postcondition is "false": * * 1. If the action is infeasible, the Hoare triple is valid. * * 2. If the action is feasible, and does not constrain any variables or constants mentioned in pre, then the Hoare * triple is invalid. */ @Override public Validity sdecToFalse(final IPredicate pre, final IPredicate preHier, final IInternalAction act) { assert preHier == null : PRE_HIER_ERROR; final var tf = act.getTransformula(); switch (tf.isInfeasible()) { case INFEASIBLE: return Validity.VALID; case UNPROVEABLE: if (varsDisjointFromInVars(pre, tf) && disjointFunctions(pre, tf) && !containsConflictingNonModifiableOldVars(act.getPrecedingProcedure(), pre)) { mStatistics.getSDtfsCounter().incIn(); return Validity.INVALID; } return null; case NOT_DETERMINED: return null; } throw new IllegalArgumentException(); } /** * If pre- and postcondition are equal, and the assigned variables of the action are disjoint from the variables in * the pre/postcondition, the Hoare triple is trivially valid. */ @Override public boolean isInductiveSelfloop(final IPredicate preLin, final IPredicate preHier, final IInternalAction act, final IPredicate succ) { assert preHier == null : PRE_HIER_ERROR; if (preLin != succ) { return false; } if (varsDisjointFromAssignedVars(preLin, act.getTransformula())) { mStatistics.getSDsluCounter().incIn(); return true; } return false; } /** * This method essentially handles 2 cases: * * 1. If pre implies post, and no relevant variables are modified by the action, then the Hoare triple is valid. * * 2. If pre does not imply post, pre and post do not constrain the variables and constants read or assigned by the * action, and the action is feasible, then the Hoare triple is invalid. * * The method assumes that the given action is not marked as infeasible. * * {@inheritDoc} */ @Override public Validity sdec(final IPredicate pre, final IPredicate preHier, final IInternalAction act, final IPredicate post) { assert preHier == null : PRE_HIER_ERROR; final UnmodifiableTransFormula tf = act.getTransformula(); // TODO In the presence of axioms, we have to check (pre /\ axiom |= post). final Validity preImpliesPost = mCoverage.isCovered(pre, post); switch (preImpliesPost) { case VALID: // If pre implies post, and act does not modify any variable in pre, then the Hoare triple is valid. // Similarly, if act does not modify any variable in post, the Hoare triple is also valid. if (varsDisjointFromAssignedVars(pre, tf) || varsDisjointFromAssignedVars(post, tf)) { mStatistics.getSDsluCounter().incIn(); return Validity.VALID; } return null; case INVALID: // continue below break; case UNKNOWN: case NOT_CHECKED: return null; default: throw new AssertionError("illegal value"); } if (!varsDisjointFromInVars(pre, tf) || !varsDisjointFromInVars(post, tf) || !varsDisjointFromAssignedVars(post, tf)) { return null; } if (!disjointFunctions(pre, tf) || !disjointFunctions(post, tf)) { return null; } // The lines below address a special case that is relevant for e.g., the following example. Let's assume that x // is a global variable, pre is `x = 42`, post is `old(x) = 42`, and the action represents an `assume true` // statement. Hence, all variables are disjoint, pre does not imply post, and at a first glance it seems like // the Hoare triple is invalid. If however the preceding and succeeding procedure of the action does not have // `x` in its modifies clause, we consider the Hoare triple to be valid. final String proc = act.getPrecedingProcedure(); if (!proc.equals(act.getSucceedingProcedure())) { assert act instanceof IIcfgForkTransitionThreadOther || act instanceof IIcfgJoinTransitionThreadOther : "internal statement must not change procedure"; // Unclear what we can do for fork and join statements. return null; } if (containsConflictingNonModifiableOldVars(proc, pre, post) || containsConflictingNonModifiableOldVars(proc, pre) || containsConflictingNonModifiableOldVars(proc, post)) { return null; } // We know that the variables of pre and post are both disjoint from the variables of act, and act does not // constrain the value of any program constants. Thus, if act is feasible, the Hoare triple must be invalid. // TODO In the presence of axioms, we need feasibility modulo the axioms. switch (act.getTransformula().isInfeasible()) { case INFEASIBLE: throw new IllegalArgumentException("case should have been handled before"); case NOT_DETERMINED: return null; case UNPROVEABLE: // FIXME only invalid if feasibility of transformula proven mStatistics.getSDsCounter().incIn(); return Validity.INVALID; default: throw new AssertionError("illegal value"); } } /** * FIXME 20210810 Matthias: Bad name: "incomplete" would be better than "lazy". Idea: If succedent of implication * does (in NNF) not contain a disjunction and contains some variable that does not occur in the antecedent the * implication does not hold very often. */ @Deprecated @Override public Validity sdLazyEc(final IPredicate preLin, final IPredicate preHier, final IInternalAction act, final IPredicate succ) { assert preHier == null : PRE_HIER_ERROR; if (isOrIteFormula(succ)) { return sdec(preLin, null, act, succ); } for (final IProgramVar bv : succ.getVars()) { if (!preLin.getVars().contains(bv) || !act.getTransformula().getInVars().containsKey(bv) || !act.getTransformula().getOutVars().containsKey(bv)) { // occurs neither in pre not in codeBlock, probably unsat mStatistics.getSdLazyCounter().incIn(); return Validity.INVALID; } } return null; } // Checks if one predicate contains a non-modifiable global variable x and the other contains old(x). private boolean containsConflictingNonModifiableOldVars(final String proc, final IPredicate p1, final IPredicate p2) { for (final var pv : p1.getVars()) { if (pv instanceof IProgramOldVar) { final var pnov = ((IProgramOldVar) pv).getNonOldVar(); if (!mModifiableGlobals.isModifiable(pnov, proc) && p2.getVars().contains(pnov)) { return true; } } else if (pv instanceof IProgramNonOldVar) { final var pnov = (IProgramNonOldVar) pv; if (!mModifiableGlobals.isModifiable(pnov, proc) && p2.getVars().contains(pnov.getOldVar())) { return true; } } } return false; } }