/* * Copyright (C) 2014-2015 Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * Copyright (C) 2012-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.boogie; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import de.uni_freiburg.informatik.ultimate.boogie.BitvectorFactory; import de.uni_freiburg.informatik.ultimate.boogie.DeclarationInformation; import de.uni_freiburg.informatik.ultimate.boogie.ast.ArrayAccessExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.ArrayStoreExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.Attribute; import de.uni_freiburg.informatik.ultimate.boogie.ast.BinaryExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.BitVectorAccessExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.BitvecLiteral; import de.uni_freiburg.informatik.ultimate.boogie.ast.BoogieASTNode; import de.uni_freiburg.informatik.ultimate.boogie.ast.BooleanLiteral; import de.uni_freiburg.informatik.ultimate.boogie.ast.Expression; import de.uni_freiburg.informatik.ultimate.boogie.ast.FunctionApplication; import de.uni_freiburg.informatik.ultimate.boogie.ast.IdentifierExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.IfThenElseExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.IntegerLiteral; import de.uni_freiburg.informatik.ultimate.boogie.ast.QuantifierExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.RealLiteral; import de.uni_freiburg.informatik.ultimate.boogie.ast.Trigger; import de.uni_freiburg.informatik.ultimate.boogie.ast.UnaryExpression; import de.uni_freiburg.informatik.ultimate.boogie.ast.VarList; import de.uni_freiburg.informatik.ultimate.boogie.type.BoogieType; import de.uni_freiburg.informatik.ultimate.core.model.models.IBoogieType; import de.uni_freiburg.informatik.ultimate.core.model.models.ILocation; import de.uni_freiburg.informatik.ultimate.core.model.services.IUltimateServiceProvider; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.ManagedScript; import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SmtUtils; import de.uni_freiburg.informatik.ultimate.logic.SMTLIBException; import de.uni_freiburg.informatik.ultimate.logic.Script; 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.BitvectorConstant.BvOp; import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedHashMap; /** * Translate a Boogie Expression into an SMT Term. Use the here defined interface IndentifierResolver to translate * identifier expressions. * * @author Matthias Heizmann, Thomas Lang * */ public class Expression2Term { private final Script mScript; private final TypeSortTranslator mTypeSortTranslator; private final IOperationTranslator mOperationTranslator; private final Boogie2SmtSymbolTable mBoogie2SmtSymbolTable; private final ManagedScript mVariableManager; private final boolean mOverapproximateFunctions = false; private final ScopedHashMap mQuantifiedVariables = new ScopedHashMap<>(); private IIdentifierTranslator[] mSmtIdentifierProviders; private Map mOverapproximations = null; private Collection mAuxVars = null; /** * Count the height of current old(.) expressions. As long as this is strictly greater than zero we are have to * consider all global vars as oldvars. */ private int mOldContextScopeDepth = 0; private final IUltimateServiceProvider mServices; private static final String OVERAPPROXIMATION = "overapproximation"; public Expression2Term(final IUltimateServiceProvider services, final Script script, final TypeSortTranslator typeSortTranslator, final Boogie2SmtSymbolTable boogie2SmtSymbolTable, final IOperationTranslator operationTranslator, final ManagedScript variableManager) { super(); mServices = services; mScript = script; mTypeSortTranslator = typeSortTranslator; mBoogie2SmtSymbolTable = boogie2SmtSymbolTable; mOperationTranslator = operationTranslator; mVariableManager = variableManager; } public SingleTermResult translateToTerm(final IIdentifierTranslator[] identifierTranslators, final Expression expression) { assert mSmtIdentifierProviders == null : getClass().getSimpleName() + " in use"; assert mQuantifiedVariables.isEmpty() : getClass().getSimpleName() + " in use"; assert mOverapproximations == null : getClass().getSimpleName() + " in use"; assert mAuxVars == null : getClass().getSimpleName() + " in use"; mSmtIdentifierProviders = identifierTranslators; mAuxVars = new ArrayList<>(); mOverapproximations = new HashMap<>(); final Term term = translate(expression); final SingleTermResult result = new SingleTermResult(mOverapproximations, mAuxVars, term); mSmtIdentifierProviders = null; mAuxVars = null; mOverapproximations = null; return result; } public MultiTermResult translateToTerms(final IIdentifierTranslator[] identifierTranslators, final Expression[] expressions) { assert mSmtIdentifierProviders == null : getClass().getSimpleName() + " in use"; assert mQuantifiedVariables.isEmpty() : getClass().getSimpleName() + " in use"; assert mOverapproximations == null : getClass().getSimpleName() + " in use"; assert mAuxVars == null : getClass().getSimpleName() + " in use"; mSmtIdentifierProviders = identifierTranslators; mAuxVars = new ArrayList<>(); mOverapproximations = new HashMap<>(); final Term[] terms = new Term[expressions.length]; for (int i = 0; i < expressions.length; i++) { terms[i] = translate(expressions[i]); } final MultiTermResult result = new MultiTermResult(mOverapproximations, mAuxVars, terms); mSmtIdentifierProviders = null; mAuxVars = null; mOverapproximations = null; return result; } private Term getSmtIdentifier(final String id, final DeclarationInformation declInfo, final boolean isOldContext, final BoogieASTNode boogieASTNode) { if (mQuantifiedVariables.containsKey(id)) { return mQuantifiedVariables.get(id); } for (final IIdentifierTranslator it : mSmtIdentifierProviders) { final Term term = it.getSmtIdentifier(id, declInfo, isOldContext, boogieASTNode); if (term != null) { return term; } } throw new AssertionError("found no translation for id " + id); } /** * We are in a context where we have to consider all global vars as oldvars if mOldContextScopeDepth is > 0. * * @return */ private boolean isOldContext() { return mOldContextScopeDepth > 0; } private Term translate(final Expression exp) { if (exp instanceof ArrayAccessExpression) { final ArrayAccessExpression arrexp = (ArrayAccessExpression) exp; final Expression[] indices = arrexp.getIndices(); Term result = translate(arrexp.getArray()); for (int i = 0; i < indices.length; i++) { final Term indexiTerm = translate(indices[i]); result = SmtUtils.select(mScript, result, indexiTerm); } return result; } else if (exp instanceof ArrayStoreExpression) { final ArrayStoreExpression arrexp = (ArrayStoreExpression) exp; final Expression[] indices = arrexp.getIndices(); assert indices.length > 0; // arrayBeforeIndex[i] represents the array, where all indices // before the i'th index have already been selected final Term[] arrayBeforeIndex = new Term[indices.length]; final Term[] indexTerm = new Term[indices.length]; arrayBeforeIndex[0] = translate(arrexp.getArray()); for (int i = 0; i < indices.length - 1; i++) { indexTerm[i] = translate(indices[i]); arrayBeforeIndex[i + 1] = SmtUtils.select(mScript, arrayBeforeIndex[i], indexTerm[i]); } indexTerm[indices.length - 1] = translate(indices[indices.length - 1]); Term result = translate(arrexp.getValue()); for (int i = indices.length - 1; i >= 0; i--) { result = SmtUtils.store(mScript, arrayBeforeIndex[i], indexTerm[i], result); } assert (result != null); assert resultContainsNoNull(result); return result; } else if (exp instanceof BinaryExpression) { final BinaryExpression binexp = (BinaryExpression) exp; final BinaryExpression.Operator op = binexp.getOperator(); // Sort sort = mSmt2Boogie.getSort(binexp.getLeft().getType()); if (op == BinaryExpression.Operator.COMPNEQ) { final String equalityFuncname = mOperationTranslator.opTranslation(BinaryExpression.Operator.COMPEQ, binexp.getLeft().getType(), binexp.getRight().getType()); final String negationFuncname = mOperationTranslator.opTranslation(UnaryExpression.Operator.LOGICNEG, BoogieType.TYPE_BOOL); return SmtUtils.unfTerm(mScript, negationFuncname, null, null, SmtUtils.unfTerm(mScript, equalityFuncname, null, null, translate(binexp.getLeft()), translate(binexp.getRight()))); } final String funcname = mOperationTranslator.opTranslation(op, binexp.getLeft().getType(), binexp.getRight().getType()); return SmtUtils.unfTerm(mScript, funcname, null, null, translate(binexp.getLeft()), translate(binexp.getRight())); } else if (exp instanceof UnaryExpression) { final UnaryExpression unexp = (UnaryExpression) exp; final UnaryExpression.Operator op = unexp.getOperator(); if (op == UnaryExpression.Operator.OLD) { mOldContextScopeDepth++; final Term term = translate(unexp.getExpr()); mOldContextScopeDepth--; return term; } final String funcname = mOperationTranslator.opTranslation(op, unexp.getExpr().getType()); return SmtUtils.unfTerm(mScript, funcname, null, null, translate(unexp.getExpr())); } else if (exp instanceof RealLiteral) { final Term result = mOperationTranslator.realTranslation((RealLiteral) exp); assert result != null; return result; } else if (exp instanceof BitvecLiteral) { final Term result = mOperationTranslator.bitvecTranslation((BitvecLiteral) exp); assert result != null; return result; } else if (exp instanceof BitVectorAccessExpression) { final BigInteger[] indices = new BigInteger[2]; indices[0] = BigInteger.valueOf(((BitVectorAccessExpression) exp).getEnd() - 1); indices[1] = BigInteger.valueOf(((BitVectorAccessExpression) exp).getStart()); final Term result = mScript.term("extract", SmtUtils.toStringArray(indices), null, translate(((BitVectorAccessExpression) exp).getBitvec())); assert result != null; return result; } else if (exp instanceof BooleanLiteral) { final Term result = mOperationTranslator.booleanTranslation((BooleanLiteral) exp); assert result != null; return result; } else if (exp instanceof FunctionApplication) { return translateFunctionApplication(((FunctionApplication) exp)); } else if (exp instanceof IdentifierExpression) { final IdentifierExpression var = (IdentifierExpression) exp; assert var.getDeclarationInformation() != null : " no declaration information"; final Term result = getSmtIdentifier(var.getIdentifier(), var.getDeclarationInformation(), isOldContext(), exp); assert result != null; return result; } else if (exp instanceof IntegerLiteral) { final Term result = mOperationTranslator.integerTranslation((IntegerLiteral) exp); assert result != null; return result; } else if (exp instanceof IfThenElseExpression) { final IfThenElseExpression ite = (IfThenElseExpression) exp; final Term cond = translate(ite.getCondition()); final Term thenPart = translate(ite.getThenPart()); final Term elsePart = translate(ite.getElsePart()); final Term result = SmtUtils.ite(mScript, cond, thenPart, elsePart); assert result != null; return result; } else if (exp instanceof QuantifierExpression) { // throw new // UnsupportedOperationException("QuantifierExpression not implemented yet"); final QuantifierExpression quant = (QuantifierExpression) exp; final String[] typeParams = quant.getTypeParams(); final VarList[] variables = quant.getParameters(); int numvars = typeParams.length; for (int i = 0; i < variables.length; i++) { numvars += variables[i].getIdentifiers().length; } final TermVariable[] vars = new TermVariable[numvars]; // TODO is this really unused code // HashMap newVars = new HashMap(); int offset = 0; // for (int j = 0; j < typeParams.length; j++) { // vars[offset] = mScript.createTermVariable("q" + // quoteId(typeParams[j]), intSort); // typeStack.push(vars[offset]); // offset++; // } mQuantifiedVariables.beginScope(); for (int i = 0; i < variables.length; i++) { final IBoogieType type = variables[i].getType().getBoogieType(); final Sort sort = mTypeSortTranslator.getSort(type, exp); for (int j = 0; j < variables[i].getIdentifiers().length; j++) { final String identifier = variables[i].getIdentifiers()[j]; final String smtVarName = "q" + Boogie2SMT.quoteId(variables[i].getIdentifiers()[j]); vars[offset] = mScript.variable(smtVarName, sort); mQuantifiedVariables.put(identifier, vars[offset]); offset++; } } final Term form = translate(quant.getSubformula()); final Attribute[] attrs = quant.getAttributes(); int numTrigs = 0; for (final Attribute a : attrs) { if (a instanceof Trigger) { numTrigs++; } } final Term[][] triggers = new Term[numTrigs][]; offset = 0; for (final Attribute a : attrs) { if (a instanceof Trigger) { final Expression[] trigs = ((Trigger) a).getTriggers(); final Term[] smttrigs = new Term[trigs.length]; for (int i = 0; i < trigs.length; i++) { final Term trig = translate(trigs[i]); // if (trig instanceof ITETerm // && ((ITETerm)trig).getTrueCase() == ONE // && ((ITETerm)trig).getFalseCase() == ZERO) // smttrigs[i] = ((ITETerm) trig).getCondition(); // else smttrigs[i] = trig; } triggers[offset++] = smttrigs; } } // throw new // UnsupportedOperationException("QuantifierExpression not implemented yet"); // identStack.pop(); // for (int j = 0; j < typeParams.length; j++) { // typeStack.pop(); // } mQuantifiedVariables.endScope(); Term result = null; try { result = quant.isUniversal() ? mScript.quantifier(Script.FORALL, vars, form, triggers) : mScript.quantifier(Script.EXISTS, vars, form, triggers); } catch (final SMTLIBException e) { if (e.getMessage().equals("Cannot create quantifier in quantifier-free logic")) { Boogie2SMT.reportUnsupportedSyntax(exp, "Setting does not support quantifiers", mServices); throw e; } throw new AssertionError(); } assert resultContainsNoNull(result); return result; } else { throw new AssertionError("Unsupported expression " + exp); } } private Term translateFunctionApplication(final FunctionApplication func) { final Term result; final Map attributes = mBoogie2SmtSymbolTable.getAttributes(func.getIdentifier()); final String overapproximation = Boogie2SmtSymbolTable.checkForAttributeDefinedIdentifier(attributes, OVERAPPROXIMATION); if (mOverapproximateFunctions || overapproximation != null) { final Sort resultSort = mTypeSortTranslator.getSort(func.getType(), func); final TermVariable auxVar = mVariableManager.constructFreshTermVariable(func.getIdentifier(), resultSort); mAuxVars.add(auxVar); mOverapproximations.put(overapproximation, func.getLocation()); result = auxVar; } else { final IBoogieType[] argumentTypes = new IBoogieType[func.getArguments().length]; for (int i = 0; i < func.getArguments().length; i++) { argumentTypes[i] = func.getArguments()[i].getType(); } final Sort[] params = new Sort[func.getArguments().length]; for (int i = 0; i < func.getArguments().length; i++) { params[i] = mTypeSortTranslator.getSort(func.getArguments()[i].getType(), func); } final Term[] parameters = new Term[func.getArguments().length]; for (int i = 0; i < func.getArguments().length; i++) { parameters[i] = translate(func.getArguments()[i]); } final String funcSymb = mOperationTranslator.funcApplication(func.getIdentifier(), argumentTypes); if (funcSymb == null) { throw new IllegalArgumentException("unknown function" + func.getIdentifier()); } final String[] indices = Boogie2SmtSymbolTable.checkForIndices(attributes); final BvOp sbo = BitvectorFactory.getSupportedBitvectorOperation(funcSymb); if (sbo == null) { result = mScript.term(funcSymb, indices, null, parameters); } else { // simplification is overkill for non-bv operations result = SmtUtils.unfTerm(mScript, funcSymb, indices, null, parameters); } } return result; } private boolean resultContainsNoNull(final Term result) { // toString crashes if the result contains a null element return result.toString() != null; } abstract static class TranslationResult { private final Map mOverappoximations; private final Collection mAuxiliaryVars; public TranslationResult(final Map overapproximations, final Collection auxiliaryVars) { super(); assert auxiliaryVars != null; mOverappoximations = overapproximations; mAuxiliaryVars = auxiliaryVars; } public Map getOverappoximations() { return mOverappoximations; } public Collection getAuxiliaryVars() { return mAuxiliaryVars; } } public static class SingleTermResult extends TranslationResult { private final Term mTerm; public SingleTermResult(final Map overapproximations, final Collection auxiliaryVars, final Term term) { super(overapproximations, auxiliaryVars); mTerm = term; } public Term getTerm() { return mTerm; } } public static class MultiTermResult extends TranslationResult { private final Term[] mTerms; public MultiTermResult(final Map overapproximations, final Collection auxiliaryVars, final Term[] terms) { super(overapproximations, auxiliaryVars); mTerms = terms; } public Term[] getTerms() { return mTerms; } } @FunctionalInterface public interface IIdentifierTranslator { Term getSmtIdentifier(String id, DeclarationInformation declInfo, boolean isOldContext, BoogieASTNode boogieASTNode); } }