/*
* Copyright (C) 2018 Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
* Copyright (C) 2018 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.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger;
import de.uni_freiburg.informatik.ultimate.core.model.services.IUltimateServiceProvider;
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.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.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.SmtSortUtils;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SmtUtils;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SmtUtils.SimplificationTechnique;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.quantifier.PartialQuantifierElimination;
import de.uni_freiburg.informatik.ultimate.logic.Annotation;
import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm;
import de.uni_freiburg.informatik.ultimate.logic.QuantifiedFormula;
import de.uni_freiburg.informatik.ultimate.logic.QuotedObject;
import de.uni_freiburg.informatik.ultimate.logic.Script;
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;
/**
* An {@link IHoareTripleChecker} that can be used to debug other {@link IHoareTripleChecker}s or things that generate
* hoare triples (i.e., abstract interpretation).
*
* It tries to generate and print additional information (e.g., models or unsat cores) if a hoare triple check fails.
*
* @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
*
*/
public final class DebuggingHoareTripleChecker implements IHoareTripleChecker {
private static final String PREFIX = DebuggingHoareTripleChecker.class.getSimpleName() + "_";
private static final String ANNOT_NAMED = ":named";
private static final String ID_PRECONDITION = PREFIX + "precond";
private static final String ID_PRECONDITION_NON_MOD_GLOBAL_EQUALITY = PREFIX + "precondNonModGlobalEquality";
private static final String ID_TRANSITION_MODIFIABLE_GLOBAL_EQUALITY = PREFIX + "modifiableVarEquality";
private static final String ID_LOCAL_VARS_ASSIGNMENT = PREFIX + "localVarsAssignment";
private static final String ID_HIERACHICAL_PRECONDITION = PREFIX + "hierarchicalPrecondition";
private static final String ID_NEGATED_POSTCONDITION = PREFIX + "notPostcond";
private static final String MSG_START_EDGE_CHECK = PREFIX + "Start";
private static final String MSG_END_EDGE_CHECK = PREFIX + "End";
private static final boolean SIMPLIFY_IF_ASSERTION_FAILS = true;
private final ManagedScript mManagedScript;
private final ModifiableGlobalsTable mModifiableGlobalVariableManager;
private final OldVarsAssignmentCache mOldVarsAssignmentCache;
private ScopedHashMap mHierConstants;
private final Deque mAssertions;
private final ILogger mLogger;
private final IUltimateServiceProvider mServices;
private Validity mExpected;
private boolean mIsLastOk;
private final boolean mGenerateUnsatCore;
public DebuggingHoareTripleChecker(final IUltimateServiceProvider services, final ILogger logger,
final CfgSmtToolkit csToolkit, final Validity expected) {
mLogger = logger;
mServices = services;
mManagedScript = csToolkit.getManagedScript();
mModifiableGlobalVariableManager = csToolkit.getModifiableGlobalsTable();
mOldVarsAssignmentCache = csToolkit.getOldVarsAssignmentCache();
mAssertions = new ArrayDeque<>();
mExpected = expected;
final Object val = mManagedScript.getScript().getOption(":produce-unsat-cores");
mGenerateUnsatCore = Boolean.parseBoolean(val.toString());
}
@Override
public HoareTripleCheckerStatisticsGenerator getStatistics() {
throw new UnsupportedOperationException(getClass().getName() + " does not support getEdgeCheckerBenchmark()");
}
@Override
public Validity checkInternal(final IPredicate pre, final IInternalAction act, final IPredicate post) {
return checkValidity(mExpected, act, pre, post, null, () -> assertAction(act), () -> assertPrecondition(pre),
() -> assertPostcondInternal(post));
}
@Override
public Validity checkCall(final IPredicate pre, final ICallAction act, final IPredicate post) {
return checkValidity(mExpected, act, pre, post, null, () -> assertAction(act), () -> assertPrecondition(pre),
() -> assertPostcondCall(post));
}
@Override
public Validity checkReturn(final IPredicate linPre, final IPredicate hierPre, final IReturnAction act,
final IPredicate postcond) {
return checkValidity(mExpected, act, linPre, postcond, hierPre, () -> assertAction(act),
() -> assertPrecondition(linPre), () -> assertHierPred(hierPre), () -> assertPostcondReturn(postcond));
}
public boolean isLastOk() {
return mIsLastOk;
}
public void setExpected(final Validity expected) {
mExpected = expected;
}
@Override
public void releaseLock() {
clearAssertionStack();
}
@SafeVarargs
private final Validity checkValidity(final Validity expected, final IAction transition, final IPredicate precond,
final IPredicate postcond, final IPredicate precondHier, final Supplier... funs) {
for (final Supplier fun : funs) {
final LBool quickCheckResult = fun.get();
if (quickCheckResult == LBool.UNSAT) {
return checkValidity(expected, Validity.VALID, transition, precond, postcond, precondHier);
}
}
final LBool isSat = mManagedScript.checkSat(this);
return checkValidity(expected, IncrementalPlicationChecker.convertLBool2Validity(isSat), transition, precond,
postcond, precondHier);
}
private Validity checkValidity(final Validity expected, final Validity actual, final IAction transition,
final IPredicate precond, final IPredicate postcond, final IPredicate precondHier) {
if (expected == Validity.NOT_CHECKED || expected == actual) {
mIsLastOk = true;
clearAssertionStack();
return actual;
}
if (actual == Validity.UNKNOWN) {
mIsLastOk = true;
logUnsoundness(transition, precond, postcond, precondHier, expected, actual);
clearAssertionStack();
return actual;
}
mIsLastOk = false;
logUnsoundness(transition, precond, postcond, precondHier, expected, actual);
clearAssertionStack();
return actual;
}
private void logUnsoundness(final IAction transition, final IPredicate precond, final IPredicate postcond,
final IPredicate precondHier, final Validity expected, final Validity actual) {
final Consumer