//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.IO;
using Microsoft.Boogie;
using Graphing;
using AI = Microsoft.AbstractInterpretationFramework;
using System.Diagnostics.Contracts;
using Microsoft.Basetypes;
using Microsoft.Boogie.VCExprAST;
namespace VC {
using Bpl = Microsoft.Boogie;
public class VCGen : ConditionGeneration {
private const bool _print_time = false;
///
/// Constructor. Initializes the theorem prover.
///
[NotDelayed]
public VCGen(Program program, string/*?*/ logFilePath, bool appendLogFile)
: base(program)
// throws ProverException
{
Contract.Requires(program != null);
this.appendLogFile = appendLogFile;
this.logFilePath = logFilePath;
implName2LazyInliningInfo = new Dictionary();
if (CommandLineOptions.Clo.LazyInlining > 0) {
this.GenerateVCsForLazyInlining(program);
}
}
private static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) {
Contract.Requires(assrt != null);
Contract.Ensures(Contract.Result() != null);
Expr expr = assrt.Expr;
Contract.Assert(expr != null);
switch (Wlp.Subsumption(assrt)) {
case CommandLineOptions.SubsumptionOption.Never:
expr = Expr.True;
break;
case CommandLineOptions.SubsumptionOption.Always:
break;
case CommandLineOptions.SubsumptionOption.NotForQuantifiers:
if (expr is QuantifierExpr) {
expr = Expr.True;
}
break;
default:
Contract.Assert(false);
throw new cce.UnreachableException(); // unexpected case
}
return new AssumeCmd(assrt.tok, expr);
}
#region LazyInlining
public class LazyInliningInfo {
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(impl != null);
Contract.Invariant(function != null);
Contract.Invariant(controlFlowVariable != null);
Contract.Invariant(assertExpr != null);
Contract.Invariant(cce.NonNullElements(interfaceVars));
Contract.Invariant(incarnationOriginMap == null || cce.NonNullDictionaryAndValues(incarnationOriginMap));
}
public Implementation impl;
public int uniqueId;
public Function function;
public Variable controlFlowVariable;
public List interfaceVars;
public Expr assertExpr;
public VCExpr vcexpr;
public Dictionary incarnationOriginMap;
public Hashtable /*Variable->Expr*/ exitIncarnationMap;
public Hashtable /*GotoCmd->returnCmd*/ gotoCmdOrigins;
public Hashtable/**/ label2absy;
public Dictionary reachVars;
public List reachVarBindings;
public Variable inputErrorVariable;
public Variable outputErrorVariable;
public LazyInliningInfo(Implementation impl, Program program, ProverContext ctxt, int uniqueId, GlobalVariable errorVariable) {
Contract.Requires(impl != null);
Contract.Requires(program != null);
Procedure proc = cce.NonNull(impl.Proc);
this.impl = impl;
this.uniqueId = uniqueId;
this.controlFlowVariable = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "cfc", Microsoft.Boogie.Type.Int));
impl.LocVars.Add(controlFlowVariable);
List interfaceVars = new List();
Expr assertExpr = new LiteralExpr(Token.NoToken, true);
Contract.Assert(assertExpr != null);
foreach (Variable v in program.GlobalVariables()) {
Contract.Assert(v != null);
interfaceVars.Add(v);
if (v.Name == "error")
inputErrorVariable = v;
}
// InParams must be obtained from impl and not proc
foreach (Variable v in impl.InParams) {
Contract.Assert(v != null);
interfaceVars.Add(v);
}
// OutParams must be obtained from impl and not proc
foreach (Variable v in impl.OutParams) {
Contract.Assert(v != null);
Constant c = new Constant(Token.NoToken,
new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type));
interfaceVars.Add(c);
Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v));
assertExpr = Expr.And(assertExpr, eqExpr);
}
if (errorVariable != null) {
proc.Modifies.Add(new IdentifierExpr(Token.NoToken, errorVariable));
}
foreach (IdentifierExpr e in proc.Modifies) {
Contract.Assert(e != null);
if (e.Decl == null)
continue;
Variable v = e.Decl;
Constant c = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, impl.Name + "_" + v.Name, v.TypedIdent.Type));
interfaceVars.Add(c);
if (v.Name == "error") {
outputErrorVariable = c;
continue;
}
Expr eqExpr = Expr.Eq(new IdentifierExpr(Token.NoToken, c), new IdentifierExpr(Token.NoToken, v));
assertExpr = Expr.And(assertExpr, eqExpr);
}
this.interfaceVars = interfaceVars;
this.assertExpr = Expr.Not(assertExpr);
VariableSeq functionInterfaceVars = new VariableSeq();
foreach (Variable v in interfaceVars) {
Contract.Assert(v != null);
functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, v.Name, v.TypedIdent.Type), true));
}
TypedIdent ti = new TypedIdent(Token.NoToken, "", Bpl.Type.Bool);
Contract.Assert(ti != null);
Formal returnVar = new Formal(Token.NoToken, ti, false);
Contract.Assert(returnVar != null);
this.function = new Function(Token.NoToken, proc.Name, functionInterfaceVars, returnVar);
ctxt.DeclareFunction(this.function, "");
}
}
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(implName2LazyInliningInfo == null || cce.NonNullDictionaryAndValues(implName2LazyInliningInfo));
}
private Dictionary implName2LazyInliningInfo;
private GlobalVariable errorVariable;
public void GenerateVCsForLazyInlining(Program program) {
Contract.Requires(program != null);
Checker checker = FindCheckerFor(null, CommandLineOptions.Clo.ProverKillTime);
Contract.Assert(checker != null);
VCExpr a = checker.VCExprGen.Integer(BigNum.ONE);
VCExpr b = checker.VCExprGen.Integer(BigNum.ONE);
VCExprNAry c = (VCExprNAry) checker.VCExprGen.ControlFlowFunctionApplication(a, b);
VCExprBoogieFunctionOp op = (VCExprBoogieFunctionOp)c.Op;
checker.TheoremProver.Context.DeclareFunction(op.Func, "");
errorVariable = new GlobalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "error", Bpl.Type.Bool)); // come up with a better name for the variable
program.TopLevelDeclarations.Add(errorVariable);
foreach (Declaration decl in program.TopLevelDeclarations) {
Contract.Assert(decl != null);
Implementation impl = decl as Implementation;
if (impl == null)
continue;
Procedure proc = cce.NonNull(impl.Proc);
if (proc.FindExprAttribute("inline") != null) {
LazyInliningInfo info = new LazyInliningInfo(impl, program, checker.TheoremProver.Context, QuantifierExpr.GetNextSkolemId(), errorVariable);
implName2LazyInliningInfo[impl.Name] = info;
ExprSeq exprs = new ExprSeq();
foreach (Variable v in program.GlobalVariables()) {
Contract.Assert(v != null);
exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v)));
}
foreach (Variable v in proc.InParams) {
Contract.Assert(v != null);
exprs.Add(new IdentifierExpr(Token.NoToken, v));
}
foreach (Variable v in proc.OutParams) {
Contract.Assert(v != null);
exprs.Add(new IdentifierExpr(Token.NoToken, v));
}
foreach (IdentifierExpr ie in proc.Modifies) {
Contract.Assert(ie != null);
if (ie.Decl == null)
continue;
exprs.Add(ie);
}
Expr freePostExpr = new NAryExpr(Token.NoToken, new FunctionCall(info.function), exprs);
proc.Ensures.Add(new Ensures(true, freePostExpr));
}
}
foreach (LazyInliningInfo info in implName2LazyInliningInfo.Values) {
Contract.Assert(info != null);
GenerateVCForLazyInlining(program, info, checker);
}
}
private void GenerateVCForLazyInlining(Program program, LazyInliningInfo info, Checker checker) {
Contract.Requires(program != null);
Contract.Requires(info != null);
Contract.Requires(checker != null);
Contract.Requires(info.impl != null);
Contract.Requires(info.impl.Proc != null);
Implementation impl = info.impl;
ConvertCFG2DAG(impl, program);
ModelViewInfo mvInfo;
info.gotoCmdOrigins = PassifyImpl(impl, program, out mvInfo);
Contract.Assert(info.exitIncarnationMap != null);
VCExpressionGenerator gen = checker.VCExprGen;
Contract.Assert(gen != null);
Boogie2VCExprTranslator translator = checker.TheoremProver.Context.BoogieExprTranslator;
Contract.Assert(translator != null);
TypecheckingContext tc = new TypecheckingContext(null);
impl.Typecheck(tc);
VCExpr vcexpr = gen.Not(LetVC(impl.Blocks[0], info.controlFlowVariable, null, checker.TheoremProver.Context));
Contract.Assert(vcexpr != null);
ResetPredecessors(impl.Blocks);
VCExpr reachvcexpr = GenerateReachVC(impl, info, checker);
vcexpr = gen.And(vcexpr, reachvcexpr);
List privateVars = new List();
foreach (Variable v in impl.LocVars) {
Contract.Assert(v != null);
privateVars.Add(translator.LookupVariable(v));
}
foreach (Variable v in impl.OutParams) {
Contract.Assert(v != null);
privateVars.Add(translator.LookupVariable(v));
}
if (privateVars.Count > 0) {
vcexpr = gen.Exists(new List(), privateVars, new List(),
new VCQuantifierInfos(impl.Name, info.uniqueId, false, null), vcexpr);
}
List interfaceExprVars = new List();
List interfaceExprs = new List();
foreach (Variable v in info.interfaceVars) {
Contract.Assert(v != null);
VCExprVar ev = translator.LookupVariable(v);
Contract.Assert(ev != null);
interfaceExprVars.Add(ev);
interfaceExprs.Add(ev);
}
Function function = cce.NonNull(info.function);
VCExpr expr = gen.Function(function, interfaceExprs);
Contract.Assert(expr != null);
if (CommandLineOptions.Clo.LazyInlining == 1) {
vcexpr = gen.Implies(expr, vcexpr);
} else {
Contract.Assert(CommandLineOptions.Clo.LazyInlining == 2);
vcexpr = gen.Eq(expr, vcexpr);
}
List triggers = new List();
List exprs = new List();
exprs.Add(expr);
VCTrigger trigger = new VCTrigger(true, exprs);
Contract.Assert(trigger != null);
triggers.Add(trigger);
Expr e = new LiteralExpr(Token.NoToken, BigNum.FromInt(1));
QKeyValue q = new QKeyValue(Token.NoToken, "weight", new List