/*
* This file is part of the OpenJML project.
* Author: David R. Cok
*/
package org.jmlspecs.openjml.esc;
import java.util.*;
import org.jmlspecs.openjml.*;
import org.jmlspecs.openjml.JmlTree.JmlBBArrayAccess;
import org.jmlspecs.openjml.JmlTree.JmlBBArrayAssignment;
import org.jmlspecs.openjml.JmlTree.JmlBBFieldAssignment;
import org.jmlspecs.openjml.JmlTree.JmlBinary;
import org.jmlspecs.openjml.JmlTree.JmlChoose;
import org.jmlspecs.openjml.JmlTree.JmlClassDecl;
import org.jmlspecs.openjml.JmlTree.JmlCompilationUnit;
import org.jmlspecs.openjml.JmlTree.JmlMethodSig;
import org.jmlspecs.openjml.JmlTree.JmlDoWhileLoop;
import org.jmlspecs.openjml.JmlTree.JmlEnhancedForLoop;
import org.jmlspecs.openjml.JmlTree.JmlForLoop;
import org.jmlspecs.openjml.JmlTree.JmlGroupName;
import org.jmlspecs.openjml.JmlTree.JmlImport;
import org.jmlspecs.openjml.JmlTree.JmlLblExpression;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseCallable;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseConditional;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseDecl;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseExpr;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseGroup;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseSignals;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseSignalsOnly;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseStoreRef;
import org.jmlspecs.openjml.JmlTree.JmlMethodDecl;
import org.jmlspecs.openjml.JmlTree.JmlMethodInvocation;
import org.jmlspecs.openjml.JmlTree.JmlMethodSpecs;
import org.jmlspecs.openjml.JmlTree.JmlModelProgramStatement;
import org.jmlspecs.openjml.JmlTree.JmlPrimitiveTypeTree;
import org.jmlspecs.openjml.JmlTree.JmlQuantifiedExpr;
import org.jmlspecs.openjml.JmlTree.JmlSetComprehension;
import org.jmlspecs.openjml.JmlTree.JmlSingleton;
import org.jmlspecs.openjml.JmlTree.JmlSpecificationCase;
import org.jmlspecs.openjml.JmlTree.JmlStatementDecls;
import org.jmlspecs.openjml.JmlTree.JmlStatementExpr;
import org.jmlspecs.openjml.JmlTree.JmlStatementLoop;
import org.jmlspecs.openjml.JmlTree.JmlStatementSpec;
import org.jmlspecs.openjml.JmlTree.JmlStoreRefArrayRange;
import org.jmlspecs.openjml.JmlTree.JmlStoreRefKeyword;
import org.jmlspecs.openjml.JmlTree.JmlStoreRefListExpression;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseConditional;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseConstraint;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseDecl;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseExpr;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseIn;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseInitializer;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseMaps;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseMonitorsFor;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseRepresents;
import org.jmlspecs.openjml.JmlTree.JmlVariableDecl;
import org.jmlspecs.openjml.JmlTree.JmlWhileLoop;
import org.smtlib.ICommand;
import org.smtlib.ICommand.IScript;
import org.smtlib.IExpr;
import org.smtlib.IExpr.IBinding;
import org.smtlib.IExpr.IDeclaration;
import org.smtlib.IExpr.ISymbol;
import org.smtlib.ISort;
import org.smtlib.SMT;
import org.smtlib.SMT.Configuration;
import org.smtlib.command.C_assert;
import org.smtlib.command.C_check_sat;
import org.smtlib.command.C_declare_fun;
import org.smtlib.command.C_declare_sort;
import org.smtlib.command.C_define_fun;
import org.smtlib.command.C_push;
import org.smtlib.command.C_set_logic;
import org.smtlib.command.C_set_option;
import org.smtlib.impl.Factory;
import org.smtlib.impl.Script;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.JmlType;
import com.sun.tools.javac.code.JmlTypes;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssert;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCContinue;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
import com.sun.tools.javac.tree.JCTree.JCErroneous;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCForLoop;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCSkip;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSwitch;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCTypeUnion;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
/** This class translates a BasicBlock program into SMTLIB;
* a new instance of the class is needed for each BasicBlock program translated.
* <P>
* The input program is a BasicBlock program, which may consist of the
* following kinds of statements:
* <UL>
* <LI>declaration statements with or without initializers (TODO - what kinds of types)
* <LI>JML assume statements
* <LI>JML assert statements
* <LI>JML comment statements
* </UL>
* Expressions may include the following:
* <UL>
* <LI>Java operators: + - * / % comparisons bit-operations logic-operators
* <LI>field access - TODO?
* <LI>array access - TODO?
* <LI>STORE and SELECT functions using Java method class (JCMethodInvocation)
* <LI>method calls (TODO - any restrictions?)
* </UL>
*/
public class SMTTranslator extends JmlTreeScanner {
/** The compilation context */
protected Context context;
/** The error log */
final protected Log log;
/** The symbol table for this compilation context */
final protected Symtab syms;
/** The Names table for this compilation context */
final protected Names names;
/** Cached value of the types tool */
final protected javax.lang.model.util.Types types;
final protected JmlTypes jmltypes;
/** Cached instance of the JmlTreeUtils tool for the context. */
final protected JmlTreeUtils treeutils;
/** The factory for creating SMTLIB expressions */
final protected Factory F;
/** Commonly used SMTLIB expressions - using these shares structure */
final protected ISort intSort;
final protected ISort boolSort;
protected ISort realSort;
final protected IExpr.ISymbol distinctSym;
final protected IExpr.ISymbol arraySym;
final protected IExpr.ISymbol eqSym;
final protected IExpr.ISymbol impliesSym;
final protected IExpr.ISymbol selectSym;
final protected IExpr.INumeral zero;
/** Commonly used SMT terms that model Java+JML - using these shares structure */
final protected ISort refSort;
final protected ISort javaTypeSort;
final protected ISort jmlTypeSort;
final protected IExpr.ISymbol thisSym;
final protected IExpr.ISymbol nullSym;
final protected IExpr.ISymbol lengthSym;
// These are strings that are part of our modeling of Java+JML and are
// more or less arbitrary. Strings like these that are used only once
// may be simply used in place.
// Strings that are defined by SMTLIB are used explicitly in place.
public static final String NULL = "NULL";
public static final String this_ = Strings.thisName; // Must be the same as the id used in JmlAssertionAdder
public static final String REF = "REF"; // SMT sort for Java references
public static final String JAVATYPESORT = REF; // "JavaTypeSort";
public static final String JMLTYPESORT = "JMLTypeSort";
public static final String JAVASUBTYPE = "javaSubType";
public static final String JMLSUBTYPE = "jmlSubType";
public static final String arrayLength = "__JMLlength"; // array length
public static final String arrays_ = BasicBlocker2.ARRAY_BASE_NAME; // Must match BasicBlocker2
public static final String concat = "stringConcat";
public static final String nonnullelements = "nonnullelements";
public static final String arrayElemType = "__arrayElemType";
/** A convenience declaration, to avoid calling the constructor for every empty list */
public static final List<ISort> emptyList = new LinkedList<ISort>();
/** SMTLIB subexpressions - the result of each visit call */
protected IExpr result;
/** The SMTLIB script as it is being constructed */
protected IScript script;
/** Identification of the source material, such as the method being translated, for error messages */
protected String source;
/** An alias to script.commands() */
protected List<ICommand> commands;
/** A list that accumulates all the Java type constants used */
final protected Set<Type> javaTypes = new HashSet<Type>();
/** A list that accumulates all the Java type constants used */
final protected Map<String,IExpr> javaParameterizedTypes = new HashMap<String,IExpr>();
/** A list that accumulates all the Java type names as used in SMT */
final protected Set<String> javaTypeSymbols = new HashSet<String>();
/** A counter used to make String literal identifiers unique */
int stringCount = 0;
/** A counter used to make double literal identifiers unique */
int doubleCount = 0;
/** A counter used to make identifiers unique */
int uniqueCount = 0;
private int assumeCount = -2;
/** An internal field used to indicate whether we are translating expressions inside a quantified expression */
boolean inQuant = false;
/** A mapping from Java expressions to/from SMT expressions */
final public BiMap<JCExpression,IExpr> bimap = new BiMap<JCExpression,IExpr>();
/** The constructor - create a new instance for each Basic Program to be translated */
public SMTTranslator(Context context, String source) {
this.context = context;
this.source = source;
// OpenJDK tools
log = Log.instance(context);
syms = Symtab.instance(context);
names = Names.instance(context);
types = JavacTypes.instance(context);
treeutils = JmlTreeUtils.instance(context);
jmltypes = JmlTypes.instance(context);
// SMT factory and commonly used objects
F = new org.smtlib.impl.Factory();
boolSort = F.createSortExpression(F.symbol("Bool")); // From SMT
intSort = F.createSortExpression(F.symbol("Int")); // From SMT
arraySym = F.symbol("Array"); // From SMT Array theory
eqSym = F.symbol("="); // Name determined by SMT Core theory
distinctSym = F.symbol("distinct"); // Name determined by SMT Core theory
impliesSym = F.symbol("=>"); // Name determined by SMT Core theory
selectSym = F.symbol("select"); // Name determined by SMT Array Theory
zero = F.numeral(0);
// Names used in mapping Java/JML to SMT
refSort = F.createSortExpression(F.symbol(REF));
nullSym = F.symbol(NULL);
thisSym = F.symbol(this_);
javaTypeSort = F.createSortExpression(F.symbol(JAVATYPESORT));
jmlTypeSort = F.createSortExpression(F.symbol(JMLTYPESORT));
lengthSym = F.symbol(arrayLength);
}
/** Adds symbols for the theory of Reals, if they are used */
protected void addReal() {
if (realSort == null) {
realSort = F.createSortExpression(F.symbol("Real")); // From SMT
List<ISort> args = Arrays.asList(refSort); // List of one element
ICommand c = new C_declare_fun(F.symbol("realValue"),args, realSort);
commands.add(c);
}
}
/** Adds all the definitions and axioms regarding the types used in the program */
protected int addTypeModel(SMT smt) {
List<ISort> args = Arrays.asList(refSort); // List of one element
ICommand c;
// (declare-sort JavaTypeSort 0)
if (JAVATYPESORT != REF) {
c = new C_declare_sort(F.symbol(JAVATYPESORT),zero);
commands.add(c);
}
String q = JmlOption.value(context, JmlOption.QUANTS_FOR_TYPES);
boolean quants = false;
if ("true".equals(q)) quants = true;
else if ("false".equals(q)) quants = false;
else {
boolean b = JmlOption.isOption(context, JmlOption.MINIMIZE_QUANTIFICATIONS);
quants = false; // FIXME - set true if there are any type variables
}
// (declare-sort JMLTypeSort 0)
c = new C_declare_sort(F.symbol(JMLTYPESORT),zero);
commands.add(c);
// (declare-fun javaTypeOf (REF) JAVATYPESORT))
c = new C_declare_fun(F.symbol("javaTypeOf"),args, javaTypeSort);
commands.add(c);
// (declare-fun jmlTypeOf (REF) JMLTYPESORT))
c = new C_declare_fun(F.symbol("jmlTypeOf"),args, jmlTypeSort);
commands.add(c);
c = new C_declare_fun(F.symbol("typearg1_1"), Arrays.asList(new ISort[]{jmlTypeSort}), jmlTypeSort);
commands.add(c);
c = new C_declare_fun(F.symbol("typearg2_1"), Arrays.asList(new ISort[]{jmlTypeSort}), jmlTypeSort);
commands.add(c);
c = new C_declare_fun(F.symbol("typearg2_2"), Arrays.asList(new ISort[]{jmlTypeSort}), jmlTypeSort);
commands.add(c);
// (declare-fun JAVASUBTYPE (JAVATYPESORT JAVATYPESORT) Bool)
c = new C_declare_fun(F.symbol(JAVASUBTYPE),
Arrays.asList(new ISort[]{javaTypeSort,javaTypeSort}),
boolSort);
commands.add(c);
// (declare-fun JMLSUBTYPE (JMLTYPESORT JMLTYPESORT) Bool)
c = new C_declare_fun(F.symbol(JMLSUBTYPE),
Arrays.asList(new ISort[]{jmlTypeSort,jmlTypeSort}),
boolSort);
commands.add(c);
// (declare-fun erasure (JMLTYPESORT) JAVATYPESORT)
c = new C_declare_fun(F.symbol("erasure"),
Arrays.asList(new ISort[]{jmlTypeSort}),
javaTypeSort);
commands.add(c);
addCommand(smt,"(declare-fun _JMLT_0 ("+JAVATYPESORT+") "+JMLTYPESORT+")");
addCommand(smt,"(declare-fun _JMLT_1 ("+JAVATYPESORT+" "+JMLTYPESORT+") "+JMLTYPESORT+")");
addCommand(smt,"(declare-fun _JMLT_2 ("+JAVATYPESORT+" "+JMLTYPESORT+" "+JMLTYPESORT+") "+JMLTYPESORT+")");
addCommand(smt,"(assert (forall ((o REF)) (= (erasure (jmlTypeOf o)) (javaTypeOf o))))");
addCommand(smt,"(declare-fun _makeArrayType ("+JAVATYPESORT+") "+JAVATYPESORT+")");
addCommand(smt,"(declare-fun _isArrayType ("+JAVATYPESORT+") Bool)");
addCommand(smt,"(declare-fun _makeJMLArrayType ("+JMLTYPESORT+") "+JMLTYPESORT+")");
addCommand(smt,"(declare-fun _isJMLArrayType ("+JMLTYPESORT+") Bool)");
addCommand(smt,"(declare-fun "+arrayElemType+" ("+JMLTYPESORT+") "+JMLTYPESORT+")");
addCommand(smt,"(assert (forall ((T "+JMLTYPESORT+")) (= (erasure (_makeJMLArrayType T)) (_makeArrayType (erasure T)))))");
addCommand(smt,"(assert (forall ((T1 "+JMLTYPESORT+")(T2 "+JMLTYPESORT+")) (=> ("+JMLSUBTYPE+" T1 T2) ("+JAVASUBTYPE+" (erasure T1) (erasure T2)))))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(T3 "+JMLTYPESORT+")) (= ("+JAVASUBTYPE+" T1 T2) ("+JMLSUBTYPE+" (_JMLT_1 T1 T3) (_JMLT_1 T2 T3)))))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(T3 "+JMLTYPESORT+")(T4 "+JMLTYPESORT+")) (=> (and ("+JAVASUBTYPE+" T1 T2) (not (= T3 T4))) (not ("+JMLSUBTYPE+" (_JMLT_1 T1 T3) (_JMLT_1 T2 T4))))))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(T3 "+JMLTYPESORT+")(T4 "+JMLTYPESORT+")) (=> ("+JMLSUBTYPE+" (_JMLT_1 T1 T3) (_JMLT_1 T2 T4)) (and ("+JAVASUBTYPE+" T1 T2) (= T3 T4)) ) )))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(T3 "+JMLTYPESORT+")(T4 "+JMLTYPESORT+")) (=> (= (_JMLT_1 T1 T3) (_JMLT_1 T2 T4)) (and (= T1 T2) (= T3 T4)) ) )))");
//addCommand(smt,"(assert (forall ((T "+JAVATYPESORT+")) (= ( "+arrayElemType+" (_makeArrayType T)) T)))");
if (quants) {
addCommand(smt,"(assert (forall ((T "+JMLTYPESORT+")) (= ( "+arrayElemType+" (_makeJMLArrayType T)) T)))");
addCommand(smt,"(assert (forall ((T "+JAVATYPESORT+")) (_isArrayType (_makeArrayType T)) ))");
addCommand(smt,"(assert (forall ((T "+JMLTYPESORT+")) (_isJMLArrayType (_makeJMLArrayType T)) ))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")) (= ("+JAVASUBTYPE+" (_makeArrayType T1)(_makeArrayType T2)) ("+JAVASUBTYPE+" T1 T2))))");
addCommand(smt,"(assert (forall ((T1 "+JMLTYPESORT+")(T2 "+JMLTYPESORT+")) (= ("+JMLSUBTYPE+" (_makeJMLArrayType T1)(_makeJMLArrayType T2)) ("+JMLSUBTYPE+" T1 T2))))");
}
// The declaration + assertion form is nominally equivalent to the define_fcn form, but works better
// for SMT solvers with modest (or no) support for quantifiers (like yices2)
if (false) {
// (define_fcn nonnullelements ((a REF)(arrays (Array REF (Array Int REF))))
// Bool
// (forall ((i Int)) (=> (and (<= 0 i) (< i (length a))) (distinct NULL (select (select arrays a) i)))))
c = new C_define_fun(F.symbol(nonnullelements),
Arrays.asList(new IDeclaration[]{F.declaration(F.symbol("a"),refSort),
F.declaration(F.symbol("arrays"),
F.createSortExpression(arraySym,
refSort,
F.createSortExpression(arraySym,intSort,refSort)))}),
boolSort,
F.forall(Arrays.asList(new IDeclaration[]{F.declaration(F.symbol("i"),intSort)}),
F.fcn(impliesSym,
F.fcn(F.symbol("and"),
F.fcn(F.symbol("<="),F.numeral("0"),F.symbol("i")),
F.fcn(F.symbol("<"), F.symbol("i"), F.fcn(selectSym,lengthSym,F.symbol("a")))
),
F.fcn(distinctSym,
nullSym,
F.fcn(selectSym,
F.fcn(selectSym,F.symbol("arrays"),F.symbol("a")),
F.symbol("i"))))));
commands.add(c);
} else {
// (declare_fcn nonnullelements ((a REF)(arrays (Array REF (Array Int REF)))) Bool)
// (assert (forall ((a REF)(arrays (Array REF (Array Int REF)))) (= (nonnullelements a arrays)
// (forall ((i Int)) (=> (and (<= 0 i) (< i (length a))) (distinct NULL (select (select arrays a) i)))))
c = new C_declare_fun(F.symbol(nonnullelements),
Arrays.asList(new ISort[]{refSort,
F.createSortExpression(arraySym,
refSort,
F.createSortExpression(arraySym,intSort,refSort))}),
boolSort);
commands.add(c);
c = new C_assert(
F.forall(Arrays.asList(F.declaration(F.symbol("a"),refSort),
F.declaration(F.symbol("arrays"),
F.createSortExpression(arraySym,
refSort,
F.createSortExpression(arraySym,intSort,refSort)))
),
F.fcn(eqSym,
F.fcn(F.symbol(nonnullelements), F.symbol("a"), F.symbol("arrays")),
F.forall(Arrays.asList(F.declaration(F.symbol("i"),intSort)),
F.fcn(impliesSym,
F.fcn(F.symbol("and"),
F.fcn(F.symbol("<="),F.numeral("0"),F.symbol("i")),
F.fcn(F.symbol("<"), F.symbol("i"), F.fcn(selectSym,lengthSym,F.symbol("a")))
),
F.fcn(distinctSym,
F.symbol(NULL),
F.fcn(selectSym,F.fcn(selectSym,F.symbol("arrays"),F.symbol("a")),F.symbol("i"))
)
)
)
)));
commands.add(c);
}
if (quants) {
// (forall ((t JMLTYPESORT) (tt JMLTYPESORT)) (==> (jmlSubtype t tt) (javaSubtype (erasure t) (erasure tt))))
c = new C_assert(
F.forall(Arrays.asList(F.declaration(F.symbol("t"),jmlTypeSort),
F.declaration(F.symbol("tt"),jmlTypeSort)
),
F.fcn(impliesSym,
F.fcn(F.symbol(JMLSUBTYPE), F.symbol("t"), F.symbol("tt")),
F.fcn(F.symbol(JAVASUBTYPE), F.fcn(F.symbol("erasure"), F.symbol("t")), F.fcn(F.symbol("erasure"), F.symbol("tt")))
)));
commands.add(c);
// // (forall ((r REF)) (= (erasure (jmlTypeOf r)) (javaTypeof r)))
// c = new C_assert(
// F.forall(Arrays.asList(F.declaration(F.symbol("r"),refSort)),
// F.fcn(eqSym,
// F.fcn(F.symbol("erasure"), F.fcn(F.symbol("jmlTypeOf"), F.symbol("r"))),
// F.fcn(F.symbol("javaTypeOf"), F.symbol("r"))
// )));
// commands.add(c);
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JVT2 "+JAVATYPESORT+")(JMLT "+JMLTYPESORT+")) (distinct (_JMLT_0 JVT) (_JMLT_1 JVT2 JMLT))))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JVT2 "+JAVATYPESORT+")(JMLT "+JMLTYPESORT+")(JMLT2 "+JMLTYPESORT+")) (distinct (_JMLT_0 JVT) (_JMLT_2 JVT2 JMLT JMLT2))))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JVT2 "+JAVATYPESORT+")(JMLT "+JMLTYPESORT+")(JMLT2 "+JMLTYPESORT+")(JMLT3 "+JMLTYPESORT+")) (distinct (_JMLT_1 JVT JMLT3) (_JMLT_2 JVT2 JMLT JMLT2))))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JVT2 "+JAVATYPESORT+")(JMLT "+JMLTYPESORT+")(JMLT2 "+JMLTYPESORT+")) (= (= (_JMLT_1 JVT JMLT) (_JMLT_1 JVT2 JMLT2)) (and (= JVT JVT2) (= JMLT JMLT2)))))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JMLT "+JMLTYPESORT+")) (= (erasure (_JMLT_1 JVT JMLT)) JVT)))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JMLT "+JMLTYPESORT+")) (= (typearg1_1 (_JMLT_1 JVT JMLT)) JMLT)))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JMLT1 "+JMLTYPESORT+")(JMLT2 "+JMLTYPESORT+")) (=> (= (_JMLT_1 JVT JMLT1)(_JMLT_1 JVT JMLT2)) (= JMLT1 JMLT2))))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JMLT1 "+JMLTYPESORT+")(JMLT2 "+JMLTYPESORT+")) (= (erasure (_JMLT_2 JVT JMLT1 JMLT2)) JVT)))");
addCommand(smt,"(assert (forall ((JVT "+JAVATYPESORT+")(JMLT1 "+JMLTYPESORT+")(JMLT2 "+JMLTYPESORT+")) (= (typearg2_2 (_JMLT_2 JVT JMLT1 JMLT2)) JMLT2)))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(J1 "+JMLTYPESORT+")(J2 "+JMLTYPESORT+")) (=> (= (_JMLT_1 T1 J1)(_JMLT_1 T2 J2)) (and (= T1 T2) (= J1 J2)))))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(J1 "+JMLTYPESORT+")) (=> ("+JAVASUBTYPE+" T1 T2) ("+JMLSUBTYPE+" (_JMLT_1 T1 J1) (_JMLT_1 T2 J1) ))))"); // FIXME - this is true for collections, but necessarily always true?
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")(J1 "+JMLTYPESORT+")(J2 "+JMLTYPESORT+")) (=> (and ("+JAVASUBTYPE+" T1 T2) (distinct J1 J2)) (not ("+JMLSUBTYPE+" (_JMLT_1 T1 J1) (_JMLT_1 T2 J2) )) )))"); // FIXME - this is true for collections, but necessarily always true?
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(J1 "+JMLTYPESORT+")(J2 "+JMLTYPESORT+")) (= ("+JMLSUBTYPE+" (_JMLT_1 T1 J1)(_JMLT_1 T1 J2)) (= J1 J2))))");
addCommand(smt,"(assert (forall ((T1 "+JAVATYPESORT+")(T2 "+JAVATYPESORT+")) (=> (= T1 T2) ("+JAVASUBTYPE+" T1 T2))))");
addCommand(smt,"(assert (forall ((T1 "+JMLTYPESORT+")(T2 "+JMLTYPESORT+")) (=> (= T1 T2) ("+JMLSUBTYPE+" T1 T2))))");
addCommand(smt,"(assert (forall ((T1 "+JMLTYPESORT+")(T2 "+JMLTYPESORT+")) (=> ("+JMLSUBTYPE+" T1 T2) ("+JAVASUBTYPE+" (erasure T1) (erasure T2)))))");
}
// Record the location in the commands list at which all the type
// definitions will be inserted
return commands.size();
}
protected void addTypeRelationships(int loc, SMT smt) {
// Insert type relationships, now that we have accumulated them all
// Each type has a representation as a Java (erased type) and a JML type
int len = javaTypes.size();
List<ICommand> tcommands = new ArrayList<ICommand>(len*len*2 + 3*len);
List<IExpr> typesymbols = new ArrayList<IExpr>(len);
List<IExpr> jmltypesymbols = new ArrayList<IExpr>(len);
List<ICommand> saved = commands;
commands = tcommands;
for (Type ti: javaTypes) {
if (ti.getTag() == TypeTag.TYPEVAR) {
if (ti instanceof Type.CapturedType) continue;
tcommands.add(new C_declare_fun(
(ISymbol)jmlTypeSymbol(ti),
emptyList,
jmlTypeSort));
}
}
for (Type ti: javaTypes) {
if (ti.getTag() == TypeTag.WILDCARD) continue; // Did these already, so they are done before they are used
if (ti.getTag() == TypeTag.TYPEVAR) continue; // Did these already, so they are done before they are used
// (declare-fun tjava () JavaTypeSort)
// (declare-fun tjml () JMLTypeSort)
// (assert (= (erasure tjml) tjava))
ISymbol tisym = (ISymbol)javaTypeSymbol(ti);
tcommands.add(new C_declare_fun(
tisym,
emptyList,
javaTypeSort));
typesymbols.add(tisym);
if (!ti.tsym.type.isParameterized()) {
// Note: ti.isParameterized() is true if the type name has actual parameters
// ti.tsym.type.isParameterized() is true if the declaration has parameters
// e.g. java.util.Set is false on the first, but true on the second
ISymbol tjsym = (ISymbol)jmlTypeSymbol(ti);
tcommands.add(new C_declare_fun(
tjsym,
emptyList,
jmlTypeSort));
jmltypesymbols.add(tjsym);
}
}
for (Type ti: javaTypes) {
if (ti.getTag() == TypeTag.WILDCARD) continue; // Did these already, so they are done before they are used
if (ti.getTag() == TypeTag.TYPEVAR) continue; // Did these already, so they are done before they are used
// (declare-fun tjava () JavaTypeSort)
// (declare-fun tjml () JMLTypeSort)
// (assert (= (erasure tjml) tjava))
ISymbol tisym = (ISymbol)javaTypeSymbol(ti);
tcommands.add(new C_assert(F.fcn(F.symbol("not"),F.fcn(F.symbol("_isArrayType"), javaTypeSymbol(ti))) ));
if ((ti.tsym.flags() & Flags.FINAL) != 0) {
addCommand(smt,"(assert (forall ((t "+JAVATYPESORT+")) (=> ("+JAVASUBTYPE+" t "+tisym.toString()+") (= t "+tisym.toString()+"))))");
}
if (!ti.tsym.type.isParameterized()) {
// Note: ti.isParameterized() is true if the type name has actual parameters
// ti.tsym.type.isParameterized() is true if the declaration has parameters
// e.g. java.util.Set is false on the first, but true on the second
ISymbol tjsym = (ISymbol)jmlTypeSymbol(ti);
tcommands.add(new C_assert(F.fcn(F.symbol("not"),F.fcn(F.symbol("_isJMLArrayType"), tjsym)) ));
tcommands.add(new C_assert(F.fcn(
eqSym,
F.fcn(F.symbol("_JMLT_0"),tisym),
tjsym)));
tcommands.add(new C_assert(F.fcn(
eqSym,
F.fcn(F.symbol("erasure"),tjsym),
tisym)));
if ((ti.tsym.flags() & Flags.FINAL) != 0) {
addCommand(smt,"(assert (forall ((t "+JMLTYPESORT+")) (=> ("+JMLSUBTYPE+" t "+tjsym.toString()+") (= t "+tjsym.toString()+"))))");
}
} else {
// currently we add the symbols even if the type is parameterized,
// because it's not clear what to do differently with parameterized types;
// the code below is copied directly from the if branch
IExpr tjsym = jmlTypeSymbol(ti);
if (tjsym instanceof ISymbol) {
tcommands.add(new C_declare_fun(
(ISymbol)tjsym,
emptyList,
jmlTypeSort));
}
tcommands.add(new C_assert(F.fcn(F.symbol("not"),F.fcn(F.symbol("_isJMLArrayType"), tjsym)) ));
tcommands.add(new C_assert(F.fcn(
eqSym,
F.fcn(F.symbol("erasure"),tjsym),
tisym)));
if ((ti.tsym.flags() & Flags.FINAL) != 0) {
addCommand(smt,"(assert (forall ((t "+JMLTYPESORT+")) (=> ("+JMLSUBTYPE+" t "+tjsym.toString()+") (= t "+tjsym.toString()+"))))");
}
}
}
tcommands.add(new C_assert(F.fcn(F.symbol("distinct"),typesymbols)));
tcommands.add(new C_assert(F.fcn(F.symbol("distinct"),jmltypesymbols)));
for (Type ti: javaTypes) {
if (ti instanceof ArrayType) tcommands.add(new C_assert(F.fcn(
F.symbol(JAVASUBTYPE), javaTypeSymbol(ti), F.symbol("T_java_lang_Object"))));
}
for (Type ti: javaTypes) {
if (ti.getTag() == TypeTag.TYPEVAR) continue;
if (ti.getTag() == TypeTag.WILDCARD) continue;
for (Type tj: javaTypes) {
if (tj.getTag() == TypeTag.TYPEVAR) continue;
if (tj.getTag() == TypeTag.WILDCARD) continue;
// (assert (javaSubType t1 t2)) - or assert the negation
// (assert (jmlSubType t1jml t2jml)) - or assert the negation
boolean b;
if (ti.isPrimitive() && tj.isPrimitive()) b = types.isSameType(ti, tj);
else b = types.isSubtype(types.erasure(ti),types.erasure(tj));
IExpr comp = F.fcn(F.symbol(JAVASUBTYPE), javaTypeSymbol(ti), javaTypeSymbol(tj));
if (!b) comp = F.fcn(F.symbol("not"),comp);
tcommands.add(new C_assert(comp));
comp = F.fcn(F.symbol(JAVASUBTYPE), F.fcn(F.symbol("_makeArrayType"),javaTypeSymbol(ti)), F.fcn(F.symbol("_makeArrayType"),javaTypeSymbol(tj)));
if (!b) comp = F.fcn(F.symbol("not"),comp);
tcommands.add(new C_assert(comp));
if (!ti.tsym.type.isParameterized() && !tj.tsym.type.isParameterized() ) {
if (ti.isPrimitive() && tj.isPrimitive()) b = types.isSameType(ti, tj);
else b = types.isSubtype(ti,tj);
comp = F.fcn(F.symbol(JMLSUBTYPE), jmlTypeSymbol(ti), jmlTypeSymbol(tj));
if (!b) comp = F.fcn(F.symbol("not"),comp);
tcommands.add(new C_assert(comp));
comp = F.fcn(F.symbol(JMLSUBTYPE), F.fcn(F.symbol("_makeJMLArrayType"),jmlTypeSymbol(ti)), F.fcn(F.symbol("_makeJMLArrayType"),jmlTypeSymbol(tj)));
if (!b) comp = F.fcn(F.symbol("not"),comp);
tcommands.add(new C_assert(comp));
}
}
}
{
// Transitivity of Java subtype
// Note: We try to avoid needing this (because it is quantified)
// by instantiating all the subtype
// relationships of all of the known types, but it is still needed sometimes.
// An example is catching exceptions thrown from a method: The callee
// declares a thrown exception, but the actual exception is an unknown
// subclass (t1) of the declared type (t2). A catch block that catches
// exception (t3) that is a superclass of t2 (t2 is a subclass of t3)
// must catch t1 - so we need to infer that t1 is a subclass of t3.
//
// So far we don't seem to need transitivity of JML types (TODO)
List<IDeclaration> params = new LinkedList<IDeclaration>();
params.add(F.declaration(F.symbol("t1"),javaTypeSort));
params.add(F.declaration(F.symbol("t2"),javaTypeSort));
params.add(F.declaration(F.symbol("t3"),javaTypeSort));
IExpr e = F.forall(params,
F.fcn(F.symbol("=>"),
F.fcn(F.symbol("and"),
F.fcn(F.symbol(JAVASUBTYPE),F.symbol("t1"),F.symbol("t2")),
F.fcn(F.symbol(JAVASUBTYPE),F.symbol("t2"),F.symbol("t3"))
),
F.fcn(F.symbol(JAVASUBTYPE),F.symbol("t1"),F.symbol("t3"))
));
tcommands.add(new C_assert(e));
}
{
// Transitivity of JML subtype
// Note: We try to avoid needing this (because it is quantified)
// by instantiating all the subtype
// relationships of all of the known types, but it is still needed sometimes.
// An example is catching exceptions thrown from a method: The callee
// declares a thrown exception, but the actual exception is an unknown
// subclass (t1) of the declared type (t2). A catch block that catches
// exception (t3) that is a superclass of t2 (t2 is a subclass of t3)
// must catch t1 - so we need to infer that t1 is a subclass of t3.
//
// So far we don't seem to need transitivity of JML types (TODO)
List<IDeclaration> params = new LinkedList<IDeclaration>();
params.add(F.declaration(F.symbol("t1"),jmlTypeSort));
params.add(F.declaration(F.symbol("t2"),jmlTypeSort));
params.add(F.declaration(F.symbol("t3"),jmlTypeSort));
IExpr e = F.forall(params,
F.fcn(F.symbol("=>"),
F.fcn(F.symbol("and"),
F.fcn(F.symbol(JMLSUBTYPE),F.symbol("t1"),F.symbol("t2")),
F.fcn(F.symbol(JMLSUBTYPE),F.symbol("t2"),F.symbol("t3"))
),
F.fcn(F.symbol(JMLSUBTYPE),F.symbol("t1"),F.symbol("t3"))
));
tcommands.add(new C_assert(e));
}
List<IExpr> javatypelist = new LinkedList<IExpr>();
for (Type t: javaTypes) {
if (t.getTag() != TypeTag.TYPEVAR && t.getTag() != TypeTag.WILDCARD) {
javatypelist.add(javaTypeSymbol(t));
}
}
tcommands.add(new C_assert(F.fcn(distinctSym, javatypelist)));
List<IExpr> jmltypelist = new LinkedList<IExpr>();
for (IExpr e: javaParameterizedTypes.values()) {
jmltypelist.add(e);
}
tcommands.add(new C_assert(F.fcn(distinctSym, jmltypelist)));
for (int i=1; i<=wildcardCount; ++i) {
ISymbol sym = F.symbol("JMLTV_"+"WILD"+i);
tcommands.add(0,new C_declare_fun(sym,emptyList,jmlTypeSort));
}
// Add all the type definitions into the command script before all the uses
// of the types in the various basic block translations
commands = saved;
commands.addAll(loc,tcommands);
}
/** This is called by visit methods, and super.scan calls accept methods;
* clients should call scan instead of accept so that there is a common
* processing point as well as the type-specific processing in accept methods.
*/
@Override
public void scan(JCTree t) {
result = null;
if (t != null) {
super.scan(t);
if (result != null) {
// This mapping is used in associating original source code
// sub-expressions with SMT expressions that give their values
// in a counterexample.
if (t instanceof JCExpression) bimap.put((JCExpression)t, result);
else if (t instanceof JmlVariableDecl) {
JCIdent id = ((JmlVariableDecl)t).ident;
bimap.put(id, result);
}
}
}
}
// TODO - want to be able to produce AUFBV programs as well
// TODO - this converts the whole program into one big SMT program
// - might want the option to produce many individual programs, i.e.
// one for each assertion, or a form that accommodates push/pop/coreids etc.
public ICommand.IScript convert(BasicProgram program, SMT smt) {
script = new Script();
ICommand c;
commands = script.commands();
// FIXME - use factory for the commands?
// set any options
c = new C_set_option(F.keyword(":produce-models"),F.symbol("true"));
commands.add(c);
// set the logic
String s = JmlOption.value(context, JmlOption.LOGIC);
c = new C_set_logic(F.symbol(s));
commands.add(c);
// add background statements
// declare the sorts we use to model Java+JML
// (declare-sort REF 0)
c = new C_declare_sort(F.symbol(REF),zero);
commands.add(c);
// define NULL as a REF: (declare-fun NULL () REF)
c = new C_declare_fun(nullSym,emptyList, refSort);
commands.add(c);
// define THIS as a REF: (declare-fun THIS () REF)
c = new C_declare_fun(thisSym,emptyList, refSort);
commands.add(c);
// define stringConcat: (declare-fun stringConcat (REF,REF) REF)
c = new C_declare_fun(F.symbol(concat),Arrays.asList(refSort,refSort), refSort);
commands.add(c);
// define stringLength: (declare-fun stringLength (REF) Int)
c = new C_declare_fun(F.symbol("stringLength"),Arrays.asList(refSort), intSort); // FIXME - not sure this =is used
commands.add(c);
// THIS != NULL: (assert (distinct THIS NULL))
c = new C_assert(F.fcn(distinctSym, thisSym, nullSym)); // SMT defined name
commands.add(c);
// (assert __JMLlength () (Array REF Int))
c = new C_declare_fun(lengthSym,
emptyList,
F.createSortExpression(arraySym,refSort,intSort)
);
commands.add(c);
// array lengths are always non-negative
addCommand(smt,"(assert (forall ((o "+REF+")) (>= (select "+arrayLength+" o) 0)))");
// result of string concatenation is always non-null
addCommand(smt,"(assert (forall ((s1 "+REF+")(s2 "+REF+")) (distinct ("+concat+" s1 s2) "+NULL+")))");
// The following functions model aspects of Java+JML;
// The strings here are arbitrary except that they must not conflict with
// identifiers from the Java program as mapped into SMT identifiers
List<ISort> args = Arrays.asList(refSort); // List of one element
c = new C_declare_fun(F.symbol("asIntArray"),args, F.createSortExpression(arraySym,intSort,intSort));
commands.add(c);
c = new C_declare_fun(F.symbol("asREFArray"),args, F.createSortExpression(arraySym,intSort,refSort));
commands.add(c);
c = new C_declare_fun(F.symbol("intValue"),args, intSort);
commands.add(c);
c = new C_declare_fun(F.symbol("booleanValue"),args, boolSort);
commands.add(c);
c = new C_declare_fun(lengthSym,
Arrays.asList(new ISort[]{refSort}),
intSort);
int loc = addTypeModel(smt);
// List types that we always want defined in the SMT script, whether
// or not they are explicitly used in the input program
addType(syms.objectType);
addType(syms.exceptionType);
addType(syms.runtimeExceptionType);
// Now translate all the programs background assertions
for (JCExpression e: program.background()) {
try {
scan(e);
commands.add(new C_assert(result));
} catch (RuntimeException ee) {
// skip - error already issued // FIXME - better error recovery?
}
}
// The 'defined' set holds all Names that have already had SMT definitions issued
// We have already defined some names - record that fact.
defined.add(names.fromString(this_));
defined.add(names.fromString(arrayLength));
// Add the rest that are recorded in the basic block program
for (JCIdent id: program.declarations) {
if (defined.add(id.name)) {
try {
ISort sort = convertSort(id.type);
String nm = id.name.toString();
// FIXME - I don't think 'this' should ever get this far
if (id.sym.owner instanceof Symbol.ClassSymbol && !Utils.instance(context).isJMLStatic(id.sym) && !id.sym.name.toString().equals("this")) {
// The name is a non-static field of a class, so the sort is an SMT Array
sort = F.createSortExpression(arraySym,refSort,sort);
} else if (nm.startsWith(arrays_)) {
// FIXME - review modeling of arrays
sort = convertSort(((Type.ArrayType)id.type).getComponentType());
sort = F.createSortExpression(arraySym,intSort,sort); // The type of the index is Int
sort = F.createSortExpression(arraySym,refSort,sort);
}
ISymbol sym = F.symbol(nm);
c = new C_declare_fun(sym,emptyList,sort);
commands.add(c);
bimap.put(id,sym);
} catch (RuntimeException ee) {
// skip - error already issued// FIXME - better error recovery?
}
}
}
// add definitions
for (BasicProgram.Definition e: program.definitions()) {
try {
scan(e.value);
ISymbol sym = F.symbol(e.id.toString());
c = new C_define_fun(sym,
new LinkedList<IDeclaration>(),
convertSort(e.id.type),
result);
commands.add(c);
bimap.put(e.id,sym);
} catch (RuntimeException ee) {
// skip - error already issued // FIXME - better error recovery?
}
}
// Because blocks have forward references to later blocks, but
// backward references to variables in earlier blocks, we declare
// all the block variables first
for (BasicProgram.BasicBlock b: program.blocks()) {
ICommand cc = new C_declare_fun(F.symbol(b.id.toString()), emptyList, F.Bool());
commands.add(cc);
}
// add blocks
for (BasicProgram.BasicBlock b: program.blocks()) {
convertBasicBlock(b);
}
{
// Add an assertion that negates the start block id
LinkedList<IExpr> argss = new LinkedList<IExpr>();
argss.add(F.symbol(program.startId().name.toString()));
IExpr negStartID = F.fcn(F.symbol("not"), argss);
ICommand cc = new C_assert(negStartID);
commands.add(cc);
}
// (push 1)
ICommand cc = new C_push(F.numeral(1));
commands.add(cc);
// (assert (= __JML_AssumeCheck 0))
cc = new C_assert(F.fcn(eqSym,F.symbol(JmlAssertionAdder.assumeCheckVar),zero));
commands.add(cc);
// (push 1)
cc = new C_push(F.numeral(1));
commands.add(cc);
// (check-sat)
cc = new C_check_sat();
commands.add(cc);
addTypeRelationships(loc,smt);
return script;
}
/** Adds a command expressed as a string */
protected void addCommand(SMT smt, String command) {
try {
Configuration cf = smt.smtConfig;
ICommand c = cf.smtFactory.createParser(cf,cf.smtFactory.createSource(command,null)).parseCommand();
commands.add(c);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** The String that is the encoding of a given Type */
// Note: Various SMT solvers do not yet handle special characters
// properly - some do not like the vertical bar quotes and some don't like
// periods.
public String typeString(Type t) {
if (t.getTag() == TypeTag.ARRAY){
return typeString(((ArrayType)t).elemtype) + "_A_";
}
return t.tsym.toString().replace(".", "_");
}
// public String arrayOf(Type t) {
// return "T_" + typeString(t) + "_A_";
// }
//
// public String jmlarrayOf(Type t) {
// return "JMLT_" + typeString(t) + "_A_";
// }
/** Returns an SMT Symbol representing the given Java type */
public IExpr javaTypeSymbol(Type t) {
//String s = "|T_" + t.toString() + "|";
if (t.getTag() == TypeTag.ARRAY) {
Type comptype = ((Type.ArrayType)t).getComponentType();
IExpr e = javaTypeSymbol(comptype);
return F.fcn(F.symbol("_makeArrayType"),e);
}
if (t.getTag() == TypeTag.BOT) t = syms.objectType;
else if (!t.isPrimitive()) t = t.tsym.erasure(jmltypes);
String s = "T_" + typeString(t);
return F.symbol(s);
}
private int wildcardCount = 0;
/** Returns an SMT IExpr representing the given JML type */
public IExpr jmlTypeSymbol(Type t) {
if (t.getTag() == TypeTag.BOT) t = syms.objectType;
if (t.getTag() == TypeTag.ARRAY) {
Type comptype = ((Type.ArrayType)t).getComponentType();
IExpr e = jmlTypeSymbol(comptype);
return F.fcn(F.symbol("_makeJMLArrayType"),e);
}
if (t.getTag() == TypeTag.TYPEVAR) {
String s = "JMLTV_" + typeString(t);
return F.symbol(s);
} else if (!t.tsym.type.isParameterized() || t.getTypeArguments().isEmpty()) {
if (t instanceof Type.WildcardType || t instanceof Type.CapturedType) {
String s = "JMLTV_" + "WILD" + (++wildcardCount);
ISymbol sym = F.symbol(s);
return sym;
} else {
String s = "JMLT_" + typeString(t);
return F.symbol(s);
}
} else {
List<Type> params = t.getTypeArguments();
List<IExpr> args = new LinkedList<IExpr>();
args.add(javaTypeSymbol(t));
for (Type tt: params) {
args.add(jmlTypeSymbol(tt));
}
return F.fcn(F.symbol("_JMLT_"+params.size()), args);
}
}
/** Records a type as defined. */
public void addType(Type t) {
// FIXME - what if t is the type of an explicit null?
if (t instanceof ArrayType) {
t = ((ArrayType)t).getComponentType();
addType(t);
} else {
if (javaTypeSymbols.add(t.tsym.toString())) {
javaTypes.add(t);
// We must record the bounds, but with caution since it can cause infinite recursion, in cases such as class Test<K extends Comparable<K>, O>
if (t.getTag() == TypeTag.TYPEVAR && !(t instanceof Type.WildcardType)) {
addType( ((Type.TypeVar)t).getUpperBound() );
}
}
if (t.tsym.type.isParameterized()) { // true if is or should be parameterized
if (t.getTypeArguments().size() != 0) {
boolean ok = true;
for (Type ti: t.getTypeArguments()) {
if (ti.getTag() == TypeTag.TYPEVAR) ok = false;
if (ti.getTag() == TypeTag.WILDCARD) ok = false;
addType(ti);
}
if (ok) javaParameterizedTypes.put(t.toString(),jmlTypeSymbol(t)); // FIXME - only when fully a constant and fully parameterized?
} else {
if (javaTypeSymbols.add(t.tsym.toString())) {
javaTypes.add(t);
}
// This is an unparameterized use of a should-be-parameterized class symbol
// javaParameterizedTypes.put(t.toString(),jmlTypeSymbol(t)); // FIXME - should we make an implicit argument?
}
} else if (t.getTag() != TypeTag.TYPEVAR && t.getTag() != TypeTag.WILDCARD) {
IExpr tt = F.fcn(F.symbol("_JMLT_0"),javaTypeSymbol(t));
javaParameterizedTypes.put(tt.toString(),tt); // FIXME - only when fully a constant?
}
}
// if (t.getTag() == TypeTag.TYPEVAR && !(t instanceof Type.WildcardType)) {
// addType( ((Type.TypeVar)t).getUpperBound() );
// }
}
/** Converts a BasicBlock into SMTLIB, adding commands into the
* current 'commands' list.
*/
public void convertBasicBlock(BasicProgram.BasicBlock block) {
ListIterator<JCStatement> iter = block.statements.listIterator();
IExpr tail;
if (block.followers.isEmpty()) {
tail = F.symbol("true");
} else if (block.followers.size() == 1) {
tail = F.symbol(block.followers.get(0).id.name.toString());
} else {
ArrayList<IExpr> args = new ArrayList<IExpr>();
for (BasicProgram.BasicBlock bb: block.followers) {
args.add(F.symbol(bb.id.name.toString()));
}
tail = F.fcn(F.symbol("and"),args);
}
// First add all declarations
while (iter.hasNext()) {
convertDeclaration(iter.next());
}
// Then construct the block expression from the end to the start
// iter = block.statements.listIterator();
// tail = convertList(iter,tail);
tail = convertList(block.statements,tail);
// This would be an excellent candidate for iterating through the list of
// statements in the block in reverse order, since that is the
// natural way to construct the block expression and avoids having
// a deep call stack (of the length of a block). However, the statements
// in the block have to be translated in forward order, or auxiliary
// commands produced in their translations are added to 'commands' in
// reverse order.
// while (iter.hasPrevious()) {
// tail = convertStatement(iter.previous(),tail);
// }
LinkedList<IExpr> args = new LinkedList<IExpr>();
args.add(F.symbol(block.id.toString()));
args.add(tail);
tail = F.fcn(eqSym,args);
commands.add(new C_assert(tail));
}
/** If the statement is a variable declaration, converts it to an SMT
* declare-fun or define-fun of the appropriate sort, depending on whether
* there is an initializer or not.
* @param stat
*/
public void convertDeclaration(JCStatement stat) {
if (stat instanceof JmlVariableDecl) {
try {
JmlVariableDecl decl = (JmlVariableDecl)stat;
// convert to a declaration or definition
IExpr init = decl.init == null ? null : convertExpr(decl.init);
ISymbol sym = F.symbol(decl.name.toString());
ICommand c = init == null ?
new C_declare_fun(
sym,
emptyList,
convertSort(decl.type))
: new C_define_fun(
sym,
new LinkedList<IDeclaration>(),
convertSort(decl.type),
init);
commands.add(c);
// An identifier may be appended to a JmlVariableDecl simply
// to have an expression with which to associated an SMT value
if (decl.ident != null) bimap.put(decl.ident, sym);
} catch (RuntimeException ee) {
// skip - error already issued // FIXME - better recovery
}
}
}
/** The alternate implementation, commented out below, uses recursive calls
* to assemble the block encoding. That can give quite deep call stacks.
* Instead we iterate down the list and back, storing each expression on
* a stack. So replace the call stack with a simple expression stack.
*/
public IExpr convertList(List<JCStatement> list, IExpr tail) {
ListIterator<JCStatement> iter = list.listIterator();
Stack<IExpr> stack = new Stack<IExpr>();
while (iter.hasNext()) {
JCStatement stat = iter.next();
try {
if (stat instanceof JmlVariableDecl) {
continue;
} else if (stat instanceof JmlStatementExpr) {
JmlStatementExpr s = (JmlStatementExpr)stat;
if (s.token == JmlTokenKind.ASSUME) {
if (s.label == Label.METHOD_DEFINITION) {
JCExpression ex = s.expression;
ex = ((JmlQuantifiedExpr)ex).value;
JCExpression lhs = ((JCTree.JCBinary)ex).lhs;
JCTree.JCMethodInvocation mcall = (JCTree.JCMethodInvocation)lhs;
JCExpression nm = mcall.meth;
JCExpression rhs = ((JCTree.JCBinary)ex).rhs;
addFunctionDefinition(nm.toString(),mcall.args,rhs);
} else {
IExpr exx = convertExpr(s.expression);
stack.push(exx);
}
} else if (s.token == JmlTokenKind.ASSERT) {
IExpr exx = convertExpr(s.expression);
stack.push(exx);
} else if (s.token == JmlTokenKind.COMMENT) {
if (s.id == null || !s.id.startsWith("ACHECK")) continue;
int k = s.id.indexOf(" ");
k = Integer.valueOf(s.id.substring(k+1));
s.optionalExpression = k != assumeCount ? null : (treeutils.falseLit);;
if (k != assumeCount) continue;
IExpr exx = convertExpr(treeutils.falseLit);
stack.push(exx);
} else {
log.error("jml.internal", "Incorrect kind of token encountered when converting a BasicProgram to SMTLIB: " + s.token);
break;
}
} else {
log.error("jml.internal", "Incorrect kind of statement encountered when converting a BasicProgram to SMTLIB: " + stat.getClass());
break;
}
} catch (RuntimeException ee) {
// skip - error already issued // FIXME - better recovery
break;
}
}
while (iter.hasPrevious()) {
JCStatement stat = iter.previous();
try {
if (stat instanceof JmlVariableDecl) {
continue;
} else if (stat instanceof JmlStatementExpr) {
JmlStatementExpr s = (JmlStatementExpr)stat;
if (s.token == JmlTokenKind.ASSUME) {
if (s.label == Label.METHOD_DEFINITION) {
// skip
} else {
IExpr exx = stack.pop();
tail = F.fcn(impliesSym, exx, tail);
}
} else if (s.token == JmlTokenKind.ASSERT) {
IExpr exx = stack.pop();
// The first return is the classic translation; the second
// effectively inserts an assume after an assert. I'm not
// sure it makes any difference. TODO - evaluate this sometime.
//return F.fcn(F.symbol("and"), exx, tail);
tail = F.fcn(F.symbol("and"), exx, F.fcn(impliesSym, exx, tail));
} else if (s.token == JmlTokenKind.COMMENT) {
if (s.id == null || !s.id.startsWith("ACHECK")) continue;
int k = s.id.indexOf(" ");
k = Integer.valueOf(s.id.substring(k+1));
if (k != assumeCount) continue;
IExpr exx = stack.pop();
tail = exx;
} else {
log.error("jml.internal", "Incorrect kind of token encountered when converting a BasicProgram to SMTLIB: " + s.token);
break;
}
} else {
log.error("jml.internal", "Incorrect kind of statement encountered when converting a BasicProgram to SMTLIB: " + stat.getClass());
break;
}
} catch (RuntimeException ee) {
// skip - error already issued // FIXME - better recovery
break;
}
}
return tail;
}
// public IExpr convertList(ListIterator<JCStatement> iter, IExpr tail) {
// //Stack<IExpr> stack = new Stack<IExpr>();
//
// while (iter.hasNext()) {
// JCStatement stat = iter.next();
// try {
// if (stat instanceof JmlVariableDecl) {
// continue;
// } else if (stat instanceof JmlStatementExpr) {
// JmlStatementExpr s = (JmlStatementExpr)stat;
// if (s.token == JmlToken.ASSUME) {
// IExpr exx = convertExpr(s.expression);
// tail = convertList(iter,tail);
// return F.fcn(impliesSym, exx, tail);
// } else if (s.token == JmlToken.ASSERT) {
// IExpr exx = convertExpr(s.expression);
// tail = convertList(iter,tail);
// // The first return is the classic translation; the second
// // effectively inserts an assume after an assert. I'm not
// // sure it makes any difference. TODO - evaluate this sometime.
// //return F.fcn(F.symbol("and"), exx, tail);
// return F.fcn(F.symbol("and"), exx, F.fcn(impliesSym, exx, tail));
// } else if (s.token == JmlToken.COMMENT) {
// continue;
// } else {
// log.error("jml.internal", "Incorrect kind of token encountered when converting a BasicProgram to SMTLIB: " + s.token);
// break;
// }
// } else {
// log.error("jml.internal", "Incorrect kind of statement encountered when converting a BasicProgram to SMTLIB: " + stat.getClass());
// break;
// }
// } catch (RuntimeException ee) {
// // skip - error already issued // FIXME - better recovery
// break;
// }
// }
// return tail;
// }
/** Converts a basic block statement to an SMT expression, tacking it on
* the front of tail and returning the composite expression.
*/
public IExpr convertStatement(JCStatement stat, IExpr tail) {
try {
if (stat instanceof JmlVariableDecl) {
return tail;
} else if (stat instanceof JmlStatementExpr) {
JmlStatementExpr s = (JmlStatementExpr)stat;
if (s.token == JmlTokenKind.ASSUME) {
IExpr exx = convertExpr(s.expression);
LinkedList<IExpr> args = new LinkedList<IExpr>();
args.add(exx);
args.add(tail);
return F.fcn(impliesSym, args);
} else if (s.token == JmlTokenKind.ASSERT) {
IExpr exx = convertExpr(s.expression);
LinkedList<IExpr> args = new LinkedList<IExpr>();
args.add(exx);
args.add(tail);
return F.fcn(F.symbol("and"), args);
} else if (s.token == JmlTokenKind.COMMENT) {
return tail;
} else {
log.error("jml.internal", "Incorrect kind of token encountered when converting a BasicProgram to SMTLIB: " + s.token);
}
} else {
log.error("jml.internal", "Incorrect kind of statement encountered when converting a BasicProgram to SMTLIB: " + stat.getClass());
}
} catch (RuntimeException ee) {
// skip - error already issued // FIXME - better recovery
}
return tail;
}
// FIXME - review this - need to choose between java and jml - may depend on the artihmetic mode
/** Converts a Java/JML type into an SMT Sort */
public ISort convertSort(Type t) {
if ( t == null) {
log.error("jml.internal", "No type translation implemented when converting a BasicProgram to SMTLIB: " + t);
throw new RuntimeException();
} else {
// if (t.toString().equals("\\bigint")) Utils.stop();
TypeTag tag = t.getTag();
if (tag == TypeTag.BOOLEAN) {
return F.Bool();
} else if (tag == TypeTag.INT) {
return intSort;
} else if (tag == syms.objectType.getTag()) {
return refSort;
} else if (tag == TypeTag.SHORT) {
return intSort;
} else if (tag == TypeTag.CHAR) {
return intSort;
} else if (tag == TypeTag.BYTE) {
return intSort;
} else if (tag == TypeTag.LONG) {
return intSort;
} else if (tag == TypeTag.FLOAT) {
addReal();
return realSort;
} else if (tag == TypeTag.DOUBLE) {
addReal();
return realSort;
} else if (tag == TypeTag.ARRAY) {
return refSort;
} else if (tag == TypeTag.BOT) {
return refSort;
} else if (tag == TypeTag.NONE || tag == TypeTag.UNKNOWN){
if (t instanceof JmlType) {
JmlType jt = (JmlType)t;
if (jt.jmlTypeTag() == JmlTokenKind.BSBIGINT) return intSort;
if (jt.jmlTypeTag() == JmlTokenKind.BSREAL) return realSort;
if (jt.jmlTypeTag() == JmlTokenKind.BSTYPEUC) return jmlTypeSort;
}
// FIXME - errors
return refSort; // FIXME - just something
} else if (t instanceof Type.TypeVar) {
return refSort;
} else {
// FIXME - what gets here?
return F.createSortExpression((ISymbol)javaTypeSymbol(t)); // FIXME - use the common method for translating to type names?
// log.error("jml.internal", "No type translation implemented when converting a BasicProgram to SMTLIB: " + t);
// throw new RuntimeException();
}
}
}
/** Converts an AST expression into SMT form. */
public IExpr convertExpr(JCExpression expr) {
scan(expr);
return result;
}
// We need to be able to translate expressions
/** Issues a error message about the AST node not being implemented */
public void notImpl(JCTree tree) {
log.error("esc.not.implemented","Not yet supported expression node in converting BasicPrograms to SMTLIB: " + tree.getClass());
}
/** Issues an error message about something not being implemented */
public void notImpl(String msg) {
log.error("esc.not.implemented","Not yet supported feature in converting BasicPrograms to SMTLIB: " + msg);
}
/** Issues an error message that a particular AST node should not be being used in the input basic block program */
public void shouldNotBeCalled(JCTree tree) {
log.error("jml.internal","This node should not be present in converting BasicPrograms to SMTLIB: " + tree.getClass() + " " + tree.toString());
}
/** A set to hold the names of implicit functions that have been defined so far
* (so we don't duplicate definitions).
*/
protected Set<String> fcnsDefined = new HashSet<String>();
/** Adds a function with the given name and a definition if it is not already added. */
protected void addFcn(String newname, JCMethodInvocation tree) {
if (fcnsDefined.add(newname)) {
// Was not already present
ISymbol n = F.symbol(newname);
ISort resultSort = convertSort(tree.type);
List<ISort> argSorts = new LinkedList<ISort>();
// // Adds an argument for the receiver, if the function is not static - TODO: do we ever use this?
// if (tree.meth instanceof JCFieldAccess && ((JCFieldAccess)tree.meth).selected != null && !((JCFieldAccess)tree.meth).sym.isStatic()) {
// argSorts.add(refSort);
// }
for (JCExpression e: tree.args) {
argSorts.add(convertSort(e.type));
}
C_declare_fun c = new C_declare_fun(n,argSorts,resultSort);
commands.add(c);
}
}
protected void addFunctionDefinition(String newname, List<JCExpression> args, JCExpression expr) {
if (fcnsDefined.add(newname)) {
// Was not already present
ISymbol n = F.symbol(newname);
ISort resultSort = convertSort(expr.type);
List<IDeclaration> argDecls = new LinkedList<IDeclaration>();
// // Adds an argument for the receiver, if the function is not static - TODO: do we ever use this?
// if (tree.meth instanceof JCFieldAccess && ((JCFieldAccess)tree.meth).selected != null && !((JCFieldAccess)tree.meth).sym.isStatic()) {
// argSorts.add(refSort);
// }
for (JCExpression e: args) {
IDeclaration d = F.declaration(F.symbol(e.toString()),convertSort(e.type));
argDecls.add(d);
}
C_define_fun c = new C_define_fun(n, argDecls, resultSort, convertExpr(expr));
commands.add(c);
}
}
// FIXME - review this
@Override
public void visitApply(JCMethodInvocation tree) {
JCExpression m = tree.meth;
if (m instanceof JCIdent) {
String name = ((JCIdent)m).name.toString();
String newname = name;
addFcn(newname,tree);
List<IExpr> newargs = new LinkedList<IExpr>();
for (JCExpression arg: tree.args) {
newargs.add(convertExpr(arg));
}
if (newargs.isEmpty()) result = F.symbol(newname);
else result = F.fcn(F.symbol(newname),newargs);
return;
} else if (m == null) {
if (tree instanceof JmlBBFieldAssignment) {
IExpr.IFcnExpr right = F.fcn(F.symbol("store"),
convertExpr(tree.args.get(1)),
convertExpr(tree.args.get(2)),
convertExpr(tree.args.get(3))
);
result = F.fcn(eqSym, convertExpr(tree.args.get(0)),right);
return;
}
else if (tree instanceof JmlBBArrayAssignment) {
if (tree.args.length() > 3) {
// [0] = store([1],[2], store(select([1],[2]),[3],[4]))
IExpr.IFcnExpr sel = F.fcn(selectSym,
convertExpr(tree.args.get(1)),
convertExpr(tree.args.get(2))
);
IExpr.IFcnExpr newarray = F.fcn(F.symbol("store"),
sel,
convertExpr(tree.args.get(3)),
convertExpr(tree.args.get(4))
);
IExpr.IFcnExpr right = F.fcn(F.symbol("store"),
convertExpr(tree.args.get(1)),
convertExpr(tree.args.get(2)),
newarray
);
result = F.fcn(eqSym, convertExpr(tree.args.get(0)),right);
} else {
// [0] = store([1],[2], select([0],[2]))
IExpr arg0 = convertExpr(tree.args.get(0));
IExpr arg2 = convertExpr(tree.args.get(2));
IExpr.IFcnExpr sel = F.fcn(selectSym,
arg0,
arg2
);
IExpr.IFcnExpr newarray = F.fcn(F.symbol("store"),
convertExpr(tree.args.get(1)),
arg2,
sel
);
result = F.fcn(eqSym, arg0,newarray);
}
return;
}
} else if (m instanceof JCFieldAccess) {
JCFieldAccess fa = (JCFieldAccess)m;
String name = fa.name.toString();
String newname = null;
if (Utils.instance(context).isJMLStatic(fa.sym)) {
// FIXME The fully qualifiedness should be done in BasicBlocking
newname = "_" + m.toString();
addFcn(newname,tree);
} else {
// FIXME - the non-static should have a fiully qualified name as well
newname = name;
addFcn(newname,tree);
}
List<IExpr> newargs = new LinkedList<IExpr>();
if (!Utils.instance(context).isJMLStatic(fa.sym)) newargs.add(convertExpr(fa.selected));
for (JCExpression arg: tree.args) {
newargs.add(convertExpr(arg));
}
result = F.fcn(F.symbol(newname),newargs);
}
}
/** Converts a JML function-like expression:
* \type, \typeof, <:, \nonnullelements
*
*/
@Override
public void visitJmlMethodInvocation(JmlMethodInvocation that) {
if (that.token == JmlTokenKind.BSTYPELC) {
if (that.toString().equals("\\typej(short)")) Utils.stop();
Type t = that.args.get(0).type;
addType(t);
result = that.javaType ? javaTypeSymbol(t) : jmlTypeSymbol(t);
return;
}
List<IExpr> newargs = new LinkedList<IExpr>();
for (JCExpression e: that.args) {
scan(e);
newargs.add(result);
}
if (that.token == JmlTokenKind.SUBTYPE_OF) {
result = F.fcn(F.symbol(JMLSUBTYPE), newargs);
} else if (that.token == JmlTokenKind.JSUBTYPE_OF) {
result = F.fcn(F.symbol(JAVASUBTYPE), newargs);
} else if (that.token == JmlTokenKind.BSTYPEOF) {
ISymbol s = that.javaType ? F.symbol("javaTypeOf") : F.symbol("jmlTypeOf");
result = F.fcn(s, newargs);
} else if (that.token == JmlTokenKind.BSNONNULLELEMENTS) {
result = F.fcn(F.symbol(nonnullelements), newargs);
} else if (that.token == JmlTokenKind.BSELEMTYPE) {
result = F.fcn(F.symbol(arrayElemType), newargs);
} else if (that.token == JmlTokenKind.BSERASURE) {
result = F.fcn(F.symbol("erasure"), newargs);
} else if (that.token == JmlTokenKind.BSDISTINCT) {
result = F.fcn(distinctSym, newargs);
} else if (that.meth != null) {
// Built-in methods
String n = that.meth.toString();
if (n.equals("shortValue") || n.equals("byteValue") || n.equals("charValue") || n.equals("longValue")) n = "intValue";
else if (n.equals("floatValue") || n.equals("doubleValue")) n = "realValue";
result = F.fcn(F.symbol(n),newargs);
} else {
result = newargs.get(0); // FIXME - this is needed for \old and \pre but a better solution should be found (cf. testLabeled)
}
}
@Override
public void visitNewClass(JCNewClass tree) {
shouldNotBeCalled(tree);
super.visitNewClass(tree);
}
@Override
public void visitNewArray(JCNewArray tree) {
shouldNotBeCalled(tree);
super.visitNewArray(tree);
}
@Override
public void visitAssign(JCAssign tree) {
shouldNotBeCalled(tree);
super.visitAssign(tree);
}
@Override
public void visitAssignop(JCAssignOp tree) {
shouldNotBeCalled(tree);
super.visitAssignop(tree);
}
@Override
public void visitUnary(JCUnary tree) {
JCTree.Tag op = tree.getTag();
IExpr arg = convertExpr(tree.arg);
LinkedList<IExpr> args = new LinkedList<IExpr>();
args.add(arg);
switch (op) {
case NOT:
result = F.fcn(F.symbol("not"), args);
break;
case NEG:
result = F.fcn(F.symbol("-"), args);
break;
default:
log.error("jml.internal","Don't know how to translate expression to SMTLIB: " + JmlPretty.write(tree));
throw new RuntimeException();
}
}
@Override public void visitParens(JCParens that) {
// Since SMT-LIB consists of S-expressions, we do not need to
// add additional parentheses for resolving precedence
super.visitParens(that);
}
@Override
public void visitBinary(JCBinary tree) {
JCTree.Tag op = tree.getTag();
if (op == JCTree.Tag.EQ && tree.lhs.toString().equals("(double)0")) Utils.stop();
IExpr lhs = convertExpr(tree.lhs);
IExpr rhs = convertExpr(tree.rhs);
LinkedList<IExpr> args = new LinkedList<IExpr>();
args.add(lhs);
args.add(rhs);
switch (op) {
case EQ:
result = F.fcn(eqSym, args);
if (result.toString().equals("(= java.lang.Short_TYPE JMLT_short)")) Utils.stop();
break;
case NE:
result = F.fcn(distinctSym, args);
break;
case AND:
result = F.fcn(F.symbol("and"), args);
break;
case OR:
result = F.fcn(F.symbol("or"), args);
break;
case LT:
result = F.fcn(F.symbol("<"), args);
break;
case LE:
result = F.fcn(F.symbol("<="), args);
break;
case GT:
result = F.fcn(F.symbol(">"), args);
break;
case GE:
result = F.fcn(F.symbol(">="), args);
break;
case PLUS:
if (tree.lhs.type.getTag() == TypeTag.CLASS) {
result = F.fcn(F.symbol(concat), args);
} else {
result = F.fcn(F.symbol("+"), args);
}
break;
case MINUS:
result = F.fcn(F.symbol("-"), args);
break;
case MUL:
result = F.fcn(F.symbol("*"), args);
break;
case DIV:
// FIXME - what kinds of primitive types should be expected
if (tree.type.getTag() == TypeTag.FLOAT)
result = F.fcn(F.symbol("/"), args);
else if (tree.type.getTag() == TypeTag.DOUBLE)
result = F.fcn(F.symbol("/"), args);
else
result = F.fcn(F.symbol("div"), args);
break;
case MOD:
result = F.fcn(F.symbol("mod"), args);
break;
// FIXME - implement bit operations
case BITAND:
if (tree.type.getTag() == TypeTag.BOOLEAN) {
result = F.fcn(F.symbol("and"), args);
} else {
notImpl("Bit-operation " + op);
}
break;
case BITOR:
if (tree.type.getTag() == TypeTag.BOOLEAN) {
result = F.fcn(F.symbol("or"), args);
} else {
notImpl("Bit-operation " + op);
}
break;
case BITXOR:
if (tree.type.getTag() == TypeTag.BOOLEAN) {
result = F.fcn(F.symbol("distinct"), args);
} else {
notImpl("Bit-operation " + op);
}
break;
case SL:
case SR:
case USR:
notImpl("Bit-operation " + op);
break;
default:
log.error("jml.internal","Don't know how to translate expression to SMTLIB: " + JmlPretty.write(tree));
throw new RuntimeException();
}
}
@Override
public void visitTypeCast(JCTypeCast tree) {
result = convertExpr(tree.expr);
if (tree.type.isPrimitive() == tree.expr.type.isPrimitive()) {
TypeTag tagr = tree.type.getTag();
TypeTag tage = tree.expr.type.getTag();
if (tagr == TypeTag.NONE || tagr == TypeTag.UNKNOWN) {
if (tage == TypeTag.NONE || tage == TypeTag.UNKNOWN) {
if (((JmlType)tree.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
if (((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
// \bigint to \bigint -- OK
} else if ( ((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
// \real to \bigint
result = F.fcn(F.symbol("to_int"), result);
} else {
// FIXME - error
}
} else if ( ((JmlType)tree.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
if (((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
// \bigint to \real
result = F.fcn(F.symbol("to_real"), result);
} else if ( ((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
// \real to \real -- OK
} else {
// FIXME - error
}
} else {
// FIXME - error
}
} else if (treeutils.isIntegral(tage)) {
if (((JmlType)tree.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
// int to \bigint -- OK
} else if ( ((JmlType)tree.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
// int to \real
result = F.fcn(F.symbol("to_real"), result);
} else {
// FIXME - error
}
} else {
if (((JmlType)tree.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
// float/double to \bigint
result = F.fcn(F.symbol("to_int"), result);
} else if ( ((JmlType)tree.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
// float/double to \real -- OK
} else {
// FIXME - error
}
}
} else if (tage == TypeTag.NONE || tage == TypeTag.UNKNOWN) {
if (treeutils.isIntegral(tagr)) {
if (((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
// \bigint to int -- OK
} else if ( ((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
// \real to int -- FIXME
result = F.fcn(F.symbol("to_int"), result);
} else {
// FIXME - error
}
} else {
if (((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSBIGINT) {
// \bigint to float/double -- FIXME
result = F.fcn(F.symbol("to_real"), result);
} else if ( ((JmlType)tree.expr.type).jmlTypeTag() == JmlTokenKind.BSREAL) {
// \real to float/double -- OK
} else {
// FIXME - error
}
}
} else if (!tree.type.isPrimitive()) {
// This is a cast from reference type to reference type, we can ignore it
} else if (tree.expr instanceof JCLiteral) {
// Cast from one primitive literal to a another primitive type
// Note that in SMT there is only Int and Real types (or bit-value types)
// any restrictions on the range of a value must already be stated using assertions
Object v = ((JCLiteral)tree.expr).getValue();
if (tage == tagr) {
// OK -- no change in type
} else if (treeutils.isIntegral(tage) == treeutils.isIntegral(tagr)) {
// Both are integral or both are floating point
// OK -- no change in SMT type
} else if (treeutils.isIntegral(tage)) {
// integral to real literal
java.math.BigInteger val = ((IExpr.INumeral)result).value();
result = makeRealValue(val.doubleValue());
} else if (!treeutils.isIntegral(tage)) {
// FIXME - cast from double to integral
} else if (tagr.ordinal() == TypeTag.DOUBLE.ordinal() || tagr.ordinal() == TypeTag.FLOAT.ordinal()) {
// Cast to real FIXME - already done?
Double d = new Double(v.toString());
result = makeRealValue(d.doubleValue());
}
} else {
// cast from primitive to primitive for an expression
boolean argIsInt = treeutils.isIntegral(tage);
boolean resultIsInt = treeutils.isIntegral(tagr);
if (argIsInt && !resultIsInt) {
// Requires int and real logic
// integral to real
result = F.fcn(F.symbol("to_real"), result);
} else if (!argIsInt && resultIsInt) {
// Requires int and real logic
// real to int
result = F.fcn(F.symbol("to_int"), result);
} else {
// no change in result
}
}
} else if (!tree.type.isPrimitive()) {
// Cast from primitive to object
log.error(tree,"jml.internal","Do not expect casts to reference type in expressions: " + JmlPretty.write(tree));
} else {
// unboxing cast from object to primitive
log.error(tree,"jml.internal","Do not expect casts from reference type in expressions: " + JmlPretty.write(tree));
TypeTag tag = tree.type.getTag();
switch (tag) {
case INT:
case LONG:
case SHORT:
case BYTE:
case CHAR:
case DOUBLE:
case FLOAT:
case BOOLEAN:
// FIXME - should this ever happen?
break;
default:
log.error(tree,"jml.internal","Unknown type tag in translating an unboxing cast: " + tag + " " + JmlPretty.write(tree));
}
}
}
@Override
public void visitTypeTest(JCInstanceOf tree) {
addType(tree.clazz.type);
IExpr e = convertExpr(tree.expr);
// instanceof is always false if the argument is null
// and javaTypeOf is not defined for null arguments
IExpr r1 = F.fcn(distinctSym, e, nullSym);
IExpr r2 = F.fcn(F.symbol(JAVASUBTYPE),
F.fcn(F.symbol("javaTypeOf"), e),
javaTypeSymbol(tree.clazz.type));
result = F.fcn(F.symbol("and"), r1, r2);
}
@Override
public void visitIndexed(JCArrayAccess tree) {
if (tree instanceof JmlBBArrayAccess) {
JmlBBArrayAccess aa = (JmlBBArrayAccess)tree;
// select(select(arraysId,a).i)
IExpr.IFcnExpr sel = F.fcn(selectSym,
convertExpr(aa.arraysId),
convertExpr(aa.indexed)
);
sel = F.fcn(selectSym,
sel,
convertExpr(aa.index)
);
result = sel;
return;
}
shouldNotBeCalled(tree);
}
@Override
public void visitConditional(JCConditional that) {
result = F.fcn(F.symbol("ite"),
convertExpr(that.cond),
convertExpr(that.truepart),
convertExpr(that.falsepart)
);
}
/** A set of names of fields that are already defined */
protected java.util.Set<Name> defined = new java.util.HashSet<Name>();
@Override
public void visitSelect(JCFieldAccess tree) {
// o.f becomes f[o] where f has sort (Array REF type)
if (tree.selected != null) {
JCExpression object = tree.selected;
Symbol field = tree.sym;
if (field != syms.lengthVar) {
IExpr.ISymbol name = F.symbol(tree.name.toString());
if (defined.add(tree.name)) {
ISort arrsort = F.createSortExpression(arraySym,refSort,convertSort(field.type));
ICommand c = new C_declare_fun(name,emptyList,arrsort);
commands.add(c);
}
if (Utils.instance(context).isJMLStatic(field)) {
result = name;
} else {
result = F.fcn(selectSym,
name,
object == null ? thisSym: convertExpr(object)
);
}
} else {
IExpr sel = convertExpr(object);
result = F.fcn(selectSym,F.symbol(arrayLength),sel);
return;
}
}
else shouldNotBeCalled(tree);
}
@Override
public void visitIdent(JCIdent tree) {
String n = tree.name.toString();
if (n.equals("length")) { // FIXME - not sure about this - length as array length is always a field name
result = F.symbol(arrayLength);
} else {
result = F.symbol(n);
}
}
protected Map<Double,String> reals = new HashMap<Double,String>();
@Override
public void visitLiteral(JCLiteral tree) {
Object v = tree.getValue();
if (tree.typetag == TypeTag.BOOLEAN) {
result = F.symbol(((Boolean)v) ?"true":"false");
} else if (tree.typetag == TypeTag.INT || tree.typetag == TypeTag.LONG || tree.typetag == TypeTag.SHORT || tree.typetag == TypeTag.BYTE) {
long k = Long.parseLong(v.toString());
result = k >= 0 ? F.numeral(k) : -k >= 0 ? F.fcn(F.symbol("-"), F.numeral(-k)) : F.fcn(F.symbol("-"), F.numeral(v.toString().substring(1)));
} else if (tree.typetag == TypeTag.CHAR) {
long k = (v instanceof Character) ? (long) ((Character)v).charValue() : Long.parseLong(v.toString());
result = F.numeral(k);
} else if (tree.typetag == TypeTag.BOT) {
result = nullSym;
} else if (tree.typetag == TypeTag.CLASS) {
// FIXME - every literal is different and we don't remember the value
ISymbol sym = F.symbol("STRINGLIT"+(++stringCount));
ICommand c = new C_declare_fun(sym,emptyList, refSort);
commands.add(c);
result = sym;
} else if (tree.typetag == TypeTag.FLOAT || tree.typetag == TypeTag.DOUBLE) {
result = makeRealValue((Double)v);
} else {
notImpl(tree);
super.visitLiteral(tree);
}
}
ISymbol makeRealValue(Double v) {
// FIXME - we don't remember the value
String id = reals.get(v);
if (id == null) {
id = "REALLIT"+(++doubleCount);
reals.put(v, id);
ISymbol sym = F.symbol(id);
addReal();
ICommand c = new C_declare_fun(sym,emptyList,realSort); // use definefun and a constant FIXME
commands.add(c);
return sym;
} else {
ISymbol sym = F.symbol(id);
return sym;
}
}
@Override public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that) { notImpl(that); } // FIXME - maybe
@Override public void visitJmlSetComprehension(JmlSetComprehension that) { notImpl(that); }
@Override public void visitJmlSingleton(JmlSingleton that) { notImpl(that); }
@Override public void visitLetExpr(LetExpr that) {
Iterator<JCVariableDecl> iter = that.defs.iterator();
result = doLet(iter,(JCExpression)that.expr);
}
// We need to create nested let expressions because the SMT let expression
// does parallel bindings of initializers to variables, while Java does
// sequential bindings.
private IExpr doLet(Iterator<JCVariableDecl> iter, JCExpression expr) {
if (iter.hasNext()) {
JCVariableDecl decl = iter.next();
IExpr.ISymbol sym = F.symbol(decl.name.toString());
IExpr e = convertExpr(decl.init);
List<IBinding> bindings = new LinkedList<IBinding>();
bindings.add(F.binding(sym,e));
return F.let(bindings, doLet(iter,expr));
} else {
return convertExpr(expr);
}
}
@Override
public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) {
boolean prev = inQuant;
try {
inQuant = true;
List<IDeclaration> params = new LinkedList<IDeclaration>();
for (JCVariableDecl decl: that.decls) {
IExpr.ISymbol sym = F.symbol(decl.name.toString());
ISort sort = convertSort(decl.type);
params.add(F.declaration(sym, sort));
}
scan(that.range);
IExpr range = result;
scan(that.value);
IExpr value = result;
if (that.op == JmlTokenKind.BSFORALL) {
if (range != null) value = F.fcn(impliesSym,range,value);
result = F.forall(params,value);
} else if (that.op == JmlTokenKind.BSEXISTS) {
if (range != null) value = F.fcn(F.symbol("and"),range,value);
result = F.exists(params,value);
} else {
notImpl("JML Quantified expression using " + that.op.internedName());
}
// Can't do this, because then the quantified expression is evaluated
// in the wrong context (I think)
if (false && !prev) {
// Because SMTLIB does not allow getValue to have arguments containing
// quantifiers, we do the following: as long as the quantified
// expression is not nested within another (technically could be
// as long as it is closed), we define a temporary variable to
// hold its value. We could use named
// SMTLIB expressions, but I'm not sure how widespread support
// for that feature is.
ISymbol tmp = F.symbol("_JMLSMT_tmp_" + (++uniqueCount));
ICommand c = new C_declare_fun(tmp,emptyList,boolSort);
commands.add(c);
c = new C_assert(F.fcn(eqSym, tmp, result));
commands.add(c);
result = tmp;
}
} finally {
inQuant = prev;
}
}
// FIXME - review and implement all these generic type visit functions,
// or declare that they should not be called
@Override
public void visitTypeIdent(JCPrimitiveTypeTree tree) {
notImpl(tree);
super.visitTypeIdent(tree);
}
@Override
public void visitTypeArray(JCArrayTypeTree tree) {
notImpl(tree);
super.visitTypeArray(tree);
}
@Override
public void visitTypeApply(JCTypeApply tree) {
notImpl(tree);
super.visitTypeApply(tree);
}
@Override
public void visitTypeUnion(JCTypeUnion tree) {
notImpl(tree);
super.visitTypeUnion(tree);
}
@Override
public void visitTypeParameter(JCTypeParameter tree) {
notImpl(tree);
super.visitTypeParameter(tree);
}
@Override
public void visitWildcard(JCWildcard tree) {
notImpl(tree);
super.visitWildcard(tree);
}
@Override
public void visitTypeBoundKind(TypeBoundKind tree) {
notImpl(tree);
super.visitTypeBoundKind(tree);
}
// These should all be translated away prior to calling the basic blocker,
// or should never be called in the first place, because they are not
// expressions
// FIXME - what about calls of anonymous classes
@Override public void visitTopLevel(JCCompilationUnit that) { shouldNotBeCalled(that); }
@Override public void visitImport(JCImport that) { shouldNotBeCalled(that); }
@Override public void visitJmlCompilationUnit(JmlCompilationUnit that) { shouldNotBeCalled(that); }
@Override public void visitJmlImport(JmlImport that) { shouldNotBeCalled(that); }
@Override public void visitMethodDef(JCMethodDecl that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodDecl(JmlMethodDecl that) { shouldNotBeCalled(that); }
@Override public void visitJmlBinary(JmlBinary that) { shouldNotBeCalled(that); }
@Override public void visitJmlChoose(JmlChoose that) { shouldNotBeCalled(that); }
@Override public void visitJmlClassDecl(JmlClassDecl that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodSig(JmlMethodSig that) { shouldNotBeCalled(that); }
@Override public void visitJmlDoWhileLoop(JmlDoWhileLoop that) { shouldNotBeCalled(that); }
@Override public void visitJmlEnhancedForLoop(JmlEnhancedForLoop that) { shouldNotBeCalled(that); }
@Override public void visitJmlForLoop(JmlForLoop that) { shouldNotBeCalled(that); }
@Override public void visitJmlGroupName(JmlGroupName that) { shouldNotBeCalled(that); }
@Override public void visitJmlLblExpression(JmlLblExpression that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseCallable(JmlMethodClauseCallable that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) { shouldNotBeCalled(that); }
@Override public void visitJmlMethodSpecs(JmlMethodSpecs that) { shouldNotBeCalled(that); }
@Override public void visitJmlModelProgramStatement(JmlModelProgramStatement that) { shouldNotBeCalled(that); }
@Override public void visitJmlSpecificationCase(JmlSpecificationCase that) { shouldNotBeCalled(that); }
@Override public void visitJmlStatementDecls(JmlStatementDecls that) { shouldNotBeCalled(that); }
@Override public void visitJmlStatementExpr(JmlStatementExpr that) { shouldNotBeCalled(that); }
@Override public void visitJmlStatementLoop(JmlStatementLoop that) { shouldNotBeCalled(that); }
@Override public void visitJmlStatementSpec(JmlStatementSpec that) { shouldNotBeCalled(that); }
@Override public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that) { shouldNotBeCalled(that); }
@Override public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) { shouldNotBeCalled(that); }
@Override public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseIn(JmlTypeClauseIn that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) { shouldNotBeCalled(that); }
@Override public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) { shouldNotBeCalled(that); }
@Override public void visitJmlVariableDecl(JmlVariableDecl that) { shouldNotBeCalled(that); }
@Override public void visitJmlWhileLoop(JmlWhileLoop that) { shouldNotBeCalled(that); }
@Override public void visitClassDef(JCClassDecl that) { shouldNotBeCalled(that); }
@Override public void visitVarDef(JCVariableDecl that) { shouldNotBeCalled(that); }
@Override public void visitSkip(JCSkip that) { shouldNotBeCalled(that); }
@Override public void visitBlock(JCBlock that) { shouldNotBeCalled(that); }
@Override public void visitDoLoop(JCDoWhileLoop that) { shouldNotBeCalled(that); }
@Override public void visitWhileLoop(JCWhileLoop that) { shouldNotBeCalled(that); }
@Override public void visitForLoop(JCForLoop that) { shouldNotBeCalled(that); }
@Override public void visitForeachLoop(JCEnhancedForLoop that) { shouldNotBeCalled(that); }
@Override public void visitLabelled(JCLabeledStatement that) { shouldNotBeCalled(that); }
@Override public void visitSwitch(JCSwitch that) { shouldNotBeCalled(that); }
@Override public void visitCase(JCCase that) { shouldNotBeCalled(that); }
@Override public void visitSynchronized(JCSynchronized that) { shouldNotBeCalled(that); }
@Override public void visitTry(JCTry that) { shouldNotBeCalled(that); }
@Override public void visitCatch(JCCatch that) { shouldNotBeCalled(that); }
@Override public void visitIf(JCIf that) { shouldNotBeCalled(that); }
@Override public void visitExec(JCExpressionStatement that) { shouldNotBeCalled(that); }
@Override public void visitBreak(JCBreak that) { shouldNotBeCalled(that); }
@Override public void visitContinue(JCContinue that) { shouldNotBeCalled(that); }
@Override public void visitReturn(JCReturn that) { shouldNotBeCalled(that); }
@Override public void visitThrow(JCThrow that) { shouldNotBeCalled(that); }
@Override public void visitAssert(JCAssert that) { shouldNotBeCalled(that); }
@Override public void visitAnnotation(JCAnnotation that) { shouldNotBeCalled(that); }
@Override public void visitModifiers(JCModifiers that) { shouldNotBeCalled(that); }
@Override public void visitErroneous(JCErroneous that) { shouldNotBeCalled(that); }
}