/* * 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.cfg; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.IProgramFunction; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.BasicPredicate; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.IPredicate; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.TermVarsFuns; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.scripttransfer.DeclarableFunctionSymbol; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.scripttransfer.HistoryRecordingScript; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.scripttransfer.ISmtDeclarable; import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.scripttransfer.NonDeclaringTermTransferrer; 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.Substitution; import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm; import de.uni_freiburg.informatik.ultimate.logic.Script; import de.uni_freiburg.informatik.ultimate.logic.Script.LBool; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.TermTransformer; /** * {@link SmtFunctionsAndAxioms} contains axioms and SMT function symbols * created throughout a toolchain. * * @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * * TODO: Extend {@link HistoryRecordingScript} to be able to create a * nicer term transferrer * * TODO 20220401 Matthias: It does not make sense to store axioms and * SMT function symbols that are created throughout a toolchain in the * ICFG. Each algorithm should store the function symbol that it * utilizes. FunctionSymbols that are utilized by the ICFG should be * stored in the symbol table of the ICFG. * */ public class SmtFunctionsAndAxioms { public static final int HARDCODED_SERIALNUMBER_FOR_AXIOMS = 0; /** * {@link Script} instance that was used to create the ICFG to which this {@link SmtFunctionsAndAxioms} instance * belongs. */ private final HistoryRecordingScript mScript; private final ManagedScript mMgdScript; private final IPredicate mAxioms; /** * Create a {@link SmtFunctionsAndAxioms} instance with no axioms. * * @param mgdScript * A {@link ManagedScript} instance that was used to build the ICFG to which this * {@link SmtFunctionsAndAxioms} instance belongs. */ public SmtFunctionsAndAxioms(final ManagedScript mgdScript) { this(mgdScript.getScript().term("true"), Collections.emptySet(), mgdScript); } /** * Create a {@link SmtFunctionsAndAxioms} instance with axioms defined by a * {@link Term} and a set of {@link IProgramFunction}s. * * @param axioms Axioms given as {@link Term}. * @param funs The procedures from which the axioms come or null. * @param script A {@link ManagedScript} instance that was used to build the * axioms term and the ICFG to which this * {@link SmtFunctionsAndAxioms} instance belongs. */ public SmtFunctionsAndAxioms(final Term axioms, final Set funs, final ManagedScript mgdScript) { this(new BasicPredicate(HARDCODED_SERIALNUMBER_FOR_AXIOMS, axioms, Collections.emptySet(), funs, axioms), mgdScript); } /** * Create a {@link SmtFunctionsAndAxioms} instance. * * @param axioms * Axioms given as {@link IPredicate} * @param script * A {@link ManagedScript} instance that was used to build the axioms and the ICFG to which this * {@link SmtFunctionsAndAxioms} instance belongs. */ public SmtFunctionsAndAxioms(final IPredicate axioms, final ManagedScript mgdScript) { mAxioms = Objects.requireNonNull(axioms); mMgdScript = mgdScript; mScript = Objects.requireNonNull( HistoryRecordingScript.extractHistoryRecordingScript(Objects.requireNonNull(mgdScript.getScript()))); assert axioms.getClosedFormula() == axioms.getFormula() : "Axioms are not closed"; assert axioms.getFormula().getFreeVars().length == 0 : "Axioms are not closed"; } /** * Create a new {@link SmtFunctionsAndAxioms} instance with an additional axiom without corresponding procedure. * Also asserts the new axiom in the underlying script. * @param symbolTable */ public SmtFunctionsAndAxioms addAxiom(final Term additionalAxioms, final IIcfgSymbolTable symbolTable) { final Term newAxioms = SmtUtils.and(mScript, mAxioms.getClosedFormula(), additionalAxioms); final LBool quickCheckAxioms = mScript.assertTerm(additionalAxioms); // TODO: Use an Ultimate result to report inconsistent axioms; we do not want to disallow inconsistent axioms, // we just want to be informed about them. assert quickCheckAxioms != LBool.UNSAT : "Axioms are inconsistent"; final TermVarsFuns tvp = TermVarsFuns.computeTermVarsFuns(newAxioms, mMgdScript, symbolTable); final IPredicate newAxiomsPred = new BasicPredicate(HARDCODED_SERIALNUMBER_FOR_AXIOMS, tvp.getFormula(), tvp.getVars(), tvp.getFuns(), tvp.getClosedFormula()); return new SmtFunctionsAndAxioms(newAxiomsPred, mMgdScript); } // TODO: We also want a transfer function that transfers only some variables s.t. trace checks can be more focused /** * Define all symbols defined by the underlying {@link Script} instance of this {@link SmtFunctionsAndAxioms} * instance in a fresh script and assert all Axioms there. * * @param script * the fresh script. */ public void transferAllSymbols(final Script script) { HistoryRecordingScript.transferHistoryFromRecord(mScript, script); final NonDeclaringTermTransferrer tt = new NonDeclaringTermTransferrer(script); final Term transferredAxiom = tt.transform(mAxioms.getClosedFormula()); if (!SmtUtils.isTrueLiteral(transferredAxiom)) { final LBool quickCheckAxioms = script.assertTerm(transferredAxiom); // TODO: Use an Ultimate result to report inconsistent axioms; we do not want to disallow inconsistent // axioms, // we just want to be informed about them. assert quickCheckAxioms != LBool.UNSAT : "Axioms are inconsistent"; } } /** * Replace all function applications of the supplied term that are contained in this {@link SmtFunctionsAndAxioms} * instance with their bodies. * * TODO: Also inline axioms. */ public Term inline(final Term term) { return new SmtFunctionInliner().inline(mMgdScript, term); } public IPredicate getAxioms() { return mAxioms; } /** * @return A map from function name to function definition containing all functions with a body of the enclosed * script. */ public Map getDefinedFunctions() { return mScript.getSymbolTable().entrySet().stream() .filter(a -> a.getValue() instanceof DeclarableFunctionSymbol) .map(a -> (DeclarableFunctionSymbol) a.getValue()).filter(a -> a.getDefinition() != null) .collect(Collectors.toMap(DeclarableFunctionSymbol::getName, a -> a)); } /** * * @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * */ private static class SmtFunctionInliner extends TermTransformer { private ManagedScript mMgdScript; private HistoryRecordingScript mScript; public Term inline(final ManagedScript mgdScript, final Term term) { mMgdScript = mgdScript; mScript = HistoryRecordingScript.extractHistoryRecordingScript(mgdScript.getScript()); if (mScript == null) { throw new IllegalArgumentException( mgdScript.getScript().getClass() + " does not contain a " + HistoryRecordingScript.class); } return transform(term); } @Override public void convertApplicationTerm(final ApplicationTerm appTerm, final Term[] newArgs) { final String funName = appTerm.getFunction().getName(); final ISmtDeclarable decl = mScript.getSymbolTable().get(funName); if (decl == null) { setResult(SmtUtils.convertApplicationTerm(appTerm, newArgs, mScript)); return; } assert decl instanceof DeclarableFunctionSymbol; final DeclarableFunctionSymbol funDecl = (DeclarableFunctionSymbol) decl; final Term body = funDecl.getDefinition(); if (body == null) { setResult(SmtUtils.convertApplicationTerm(appTerm, newArgs, mScript)); return; } if (appTerm.getParameters().length == 0) { setResult(body); return; } final Term[] paramVars = funDecl.getParamVars(); assert newArgs.length == paramVars.length; final Map substitutionMapping = new HashMap<>(); for (int i = 0; i < paramVars.length; ++i) { final Term paramVar = paramVars[i]; if (paramVar == null) { // this var does not occur in the body continue; } substitutionMapping.put(paramVar, newArgs[i]); } setResult(Substitution.apply(mMgdScript, substitutionMapping, body)); } } }