//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Diagnostics.Contracts;
using Bpl = Microsoft.Boogie;
using System.Text;
using Microsoft.Boogie;
namespace Microsoft.Dafny {
public class Translator {
[NotDelayed]
public Translator() {
Bpl.Program boogieProgram = ReadPrelude();
if (boogieProgram != null) {
sink = boogieProgram;
predef = FindPredefinedDecls(boogieProgram);
}
//base();
if (predef != null) {
cevVariables.Add(predef.HeapVarName, predef.CevHeapName);
}
}
// translation state
readonly Dictionary/*!*/ classes = new Dictionary();
readonly Dictionary/*!*/ fields = new Dictionary();
// Machinery for providing information to the Counterexample Visualizer
readonly Dictionary/*!*/ cevFilenames = new Dictionary();
readonly Dictionary/*!*/ cevLocations = new Dictionary();
readonly Dictionary/*!*/ cevVariables = new Dictionary();
[ContractInvariantMethod]
void ObjectInvariant()
{
Contract.Invariant(cce.NonNullElements(classes));
Contract.Invariant(cce.NonNullElements(fields));
Contract.Invariant(cce.NonNullElements(cevFilenames));
Contract.Invariant(cce.NonNullElements(cevLocations));
Contract.Invariant(cce.NonNullElements(cevVariables));
}
Bpl.Expr CevLocation(IToken tok)
{
Contract.Requires(tok != null);
Contract.Requires( predef != null && sink != null);
Contract.Ensures(Contract.Result() != null);
Bpl.Constant c;
if (cevLocations.TryGetValue(tok, out c)) {
Contract.Assert( c != null);
} else {
int fileId;
string filename = "#file^" + (tok.filename == null ? ".dfy" : tok.filename);
if (!cevFilenames.TryGetValue(filename, out fileId)) {
fileId = cevFilenames.Count;
cevFilenames.Add(filename, fileId);
Bpl.Constant fn = new Bpl.Constant(tok, new Bpl.TypedIdent(tok, Sanitize(filename), predef.CevTokenType), true);
sink.TopLevelDeclarations.Add(fn);
sink.TopLevelDeclarations.Add(new Bpl.Axiom(tok, FunctionCall(tok, "$file_name_is", Bpl.Type.Bool, Bpl.Expr.Literal(fileId), new Bpl.IdentifierExpr(tok, fn))));
}
c = new Bpl.Constant(tok, new Bpl.TypedIdent(tok, string.Format("#tok${0}^{1}.{2}", fileId, tok.line, tok.col), predef.CevTokenType), true);
cevLocations.Add(tok, c);
sink.TopLevelDeclarations.Add(c);
}
return new Bpl.IdentifierExpr(tok, c);
}
static string Sanitize(string s) {
Contract.Requires(s != null);
Contract.Ensures(Contract.Result() != null);
StringBuilder sb = new StringBuilder();
foreach (char ch in s) {
if (char.IsLetterOrDigit(ch) || ch == '#' || ch == '^' || ch == '$' || ch == '.' || ch == '@') {
sb.Append(ch);
} else {
sb.Append(string.Format("%{0:X4}", (int)ch));
}
}
return sb.ToString();
}
Bpl.Expr CevVariable(Bpl.IToken tok, string name)
{
Contract.Requires(tok != null);
Contract.Requires(name != null);
Contract.Requires( predef != null && sink != null);
Contract.Ensures(Contract.Result() != null);
Bpl.Constant c;
if (cevVariables.TryGetValue(name, out c)) {
Contract.Assert( c != null);
} else {
c = new Bpl.Constant(tok, new Bpl.TypedIdent(tok, string.Format("#loc.{0}", name), predef.CevTokenType), true);
cevVariables.Add(name, c);
sink.TopLevelDeclarations.Add(c);
}
return new Bpl.IdentifierExpr(tok, c);
}
readonly Bpl.Program sink;
readonly PredefinedDecls predef;
internal class PredefinedDecls {
public readonly Bpl.Type RefType;
public readonly Bpl.Type BoxType;
public readonly Bpl.Type CevTokenType;
public readonly Bpl.Type CevVariableKind;
public readonly Bpl.Type CevEventType;
private readonly Bpl.TypeSynonymDecl setTypeCtor;
private readonly Bpl.TypeCtorDecl seqTypeCtor;
readonly Bpl.TypeCtorDecl fieldName;
public readonly Bpl.Type HeapType;
public readonly string HeapVarName;
public readonly Bpl.Constant CevHeapName;
public readonly Bpl.Type ClassNameType;
public readonly Bpl.Type FieldCategoryType;
public readonly Bpl.Type DatatypeType;
public readonly Bpl.Type DtCtorId;
public readonly Bpl.Expr Null;
private readonly Bpl.Constant allocField;
[ContractInvariantMethod]
void ObjectInvariant()
{
Contract.Invariant(RefType!=null);
Contract.Invariant(BoxType != null);
Contract.Invariant(CevTokenType != null);
Contract.Invariant(CevVariableKind != null);
Contract.Invariant(CevEventType != null);
Contract.Invariant(setTypeCtor != null);
Contract.Invariant(seqTypeCtor != null);
Contract.Invariant(fieldName != null);
Contract.Invariant(HeapType != null);
Contract.Invariant(HeapVarName != null);
Contract.Invariant(CevEventType != null);
Contract.Invariant(ClassNameType != null);
Contract.Invariant(FieldCategoryType != null);
Contract.Invariant(DatatypeType != null);
Contract.Invariant(DtCtorId != null);
Contract.Invariant(Null != null);
Contract.Invariant(allocField != null);
}
public Bpl.Type SetType(IToken tok, Bpl.Type ty) {
Contract.Requires(tok != null);
Contract.Requires(ty != null);
Contract.Ensures(Contract.Result() != null);
return new Bpl.TypeSynonymAnnotation(Token.NoToken, setTypeCtor, new Bpl.TypeSeq(ty));
}
public Bpl.Type SeqType(IToken tok, Bpl.Type ty) {
Contract.Requires(tok != null);
Contract.Requires(ty != null);
Contract.Ensures(Contract.Result() != null);
return new Bpl.CtorType(Token.NoToken, seqTypeCtor, new Bpl.TypeSeq(ty));
}
public Bpl.Type FieldName(IToken tok, Bpl.Type ty) {
Contract.Requires(tok != null);
Contract.Requires(ty != null);
Contract.Ensures(Contract.Result() != null);
return new Bpl.CtorType(tok, fieldName, new Bpl.TypeSeq(ty));
}
public Bpl.IdentifierExpr Alloc(IToken tok) {
Contract.Requires(tok != null);
Contract.Ensures(Contract.Result() != null);
return new Bpl.IdentifierExpr(tok, allocField);
}
public PredefinedDecls(Bpl.TypeCtorDecl refType, Bpl.TypeCtorDecl boxType, Bpl.TypeCtorDecl cevTokenType,
Bpl.TypeCtorDecl cevVariableKind, Bpl.TypeCtorDecl cevEventType,
Bpl.TypeSynonymDecl setTypeCtor, Bpl.TypeCtorDecl seqTypeCtor, Bpl.TypeCtorDecl fieldNameType,
Bpl.GlobalVariable heap, Bpl.TypeCtorDecl classNameType, Bpl.TypeCtorDecl fieldCategoryType,
Bpl.TypeCtorDecl datatypeType, Bpl.TypeCtorDecl dtCtorId,
Bpl.Constant allocField, Bpl.Constant cevHeapNameConst) {
#region Non-null preconditions on parameters
Contract.Requires(refType != null);
Contract.Requires(boxType != null);
Contract.Requires(cevTokenType != null);
Contract.Requires(cevVariableKind != null);
Contract.Requires(cevEventType != null);
Contract.Requires(setTypeCtor != null);
Contract.Requires(seqTypeCtor != null);
Contract.Requires(fieldNameType != null);
Contract.Requires(heap != null);
Contract.Requires(classNameType != null);
Contract.Requires(fieldCategoryType != null);
Contract.Requires(datatypeType != null);
Contract.Requires(dtCtorId != null);
Contract.Requires(allocField != null);
Contract.Requires(cevHeapNameConst != null);
#endregion
Bpl.CtorType refT = new Bpl.CtorType(Token.NoToken, refType, new Bpl.TypeSeq());
this.RefType = refT;
this.BoxType = new Bpl.CtorType(Token.NoToken, boxType, new Bpl.TypeSeq());
this.CevTokenType = new Bpl.CtorType(Token.NoToken, cevTokenType, new Bpl.TypeSeq());
this.CevVariableKind = new Bpl.CtorType(Token.NoToken, cevVariableKind, new Bpl.TypeSeq());
this.CevEventType = new Bpl.CtorType(Token.NoToken, cevEventType, new Bpl.TypeSeq());
this.setTypeCtor = setTypeCtor;
this.seqTypeCtor = seqTypeCtor;
this.fieldName = fieldNameType;
this.HeapType = heap.TypedIdent.Type;
this.HeapVarName = heap.Name;
this.CevHeapName = cevHeapNameConst;
this.ClassNameType = new Bpl.CtorType(Token.NoToken, classNameType, new Bpl.TypeSeq());
this.FieldCategoryType = new Bpl.CtorType(Token.NoToken, fieldCategoryType, new Bpl.TypeSeq());
this.DatatypeType = new Bpl.CtorType(Token.NoToken, datatypeType, new Bpl.TypeSeq());
this.DtCtorId = new Bpl.CtorType(Token.NoToken, dtCtorId, new Bpl.TypeSeq());
this.allocField = allocField;
this.Null = new Bpl.IdentifierExpr(Token.NoToken, "null", refT);
}
}
static PredefinedDecls FindPredefinedDecls(Bpl.Program prog) {
Contract.Requires(prog != null);
if (prog.Resolve() != 0) {
Console.WriteLine("Error: resolution errors encountered in Dafny prelude");
return null;
}
Bpl.TypeCtorDecl refType = null;
Bpl.TypeCtorDecl cevTokenType = null;
Bpl.TypeCtorDecl cevVariableKind = null;
Bpl.TypeCtorDecl cevEventType = null;
Bpl.TypeSynonymDecl setTypeCtor = null;
Bpl.TypeCtorDecl seqTypeCtor = null;
Bpl.TypeCtorDecl fieldNameType = null;
Bpl.TypeCtorDecl classNameType = null;
Bpl.TypeCtorDecl fieldCategoryType = null;
Bpl.TypeCtorDecl datatypeType = null;
Bpl.TypeCtorDecl dtCtorId = null;
Bpl.TypeCtorDecl boxType = null;
Bpl.GlobalVariable heap = null;
Bpl.Constant allocField = null;
Bpl.Constant cevHeapNameConst = null;
foreach (Bpl.Declaration d in prog.TopLevelDeclarations) {
if (d is Bpl.TypeCtorDecl) {
Bpl.TypeCtorDecl dt = (Bpl.TypeCtorDecl)d;
if (dt.Name == "Seq") {
seqTypeCtor = dt;
} else if (dt.Name == "Field") {
fieldNameType = dt;
} else if (dt.Name == "ClassName") {
classNameType = dt;
} else if (dt.Name == "FieldCategory") {
fieldCategoryType = dt;
} else if (dt.Name == "DatatypeType") {
datatypeType = dt;
} else if (dt.Name == "DtCtorId") {
dtCtorId = dt;
} else if (dt.Name == "ref") {
refType = dt;
} else if (dt.Name == "BoxType") {
boxType = dt;
} else if (dt.Name == "$token") {
cevTokenType = dt;
} else if (dt.Name == "var_locglob") {
cevVariableKind = dt;
} else if (dt.Name == "cf_event") {
cevEventType = dt;
}
} else if (d is Bpl.TypeSynonymDecl) {
Bpl.TypeSynonymDecl dt = (Bpl.TypeSynonymDecl)d;
if (dt.Name == "Set") {
setTypeCtor = dt;
}
} else if (d is Bpl.Constant) {
Bpl.Constant c = (Bpl.Constant)d;
if (c.Name == "alloc") {
allocField = c;
} else if (c.Name == "#loc.$Heap") {
cevHeapNameConst = c;
}
} else if (d is Bpl.GlobalVariable) {
Bpl.GlobalVariable v = (Bpl.GlobalVariable)d;
if (v.Name == "$Heap") {
heap = v;
}
}
}
if (seqTypeCtor == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type Seq");
} else if (setTypeCtor == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type Set");
} else if (fieldNameType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type Field");
} else if (classNameType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type ClassName");
} else if (fieldCategoryType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type FieldCategory");
} else if (datatypeType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type DatatypeType");
} else if (dtCtorId == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type DtCtorId");
} else if (refType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type ref");
} else if (boxType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type BoxType");
} else if (cevTokenType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type $token");
} else if (cevVariableKind == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type var_locglob");
} else if (cevEventType == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of type cf_event");
} else if (heap == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of $Heap");
} else if (allocField == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of constant alloc");
} else if (cevHeapNameConst == null) {
Console.WriteLine("Error: Dafny prelude is missing declaration of constant #loc.$Heap");
} else {
return new PredefinedDecls(refType, boxType, cevTokenType, cevVariableKind, cevEventType,
setTypeCtor, seqTypeCtor, fieldNameType, heap, classNameType, fieldCategoryType, datatypeType, dtCtorId,
allocField, cevHeapNameConst);
}
return null;
}
static Bpl.Program ReadPrelude() {
//using (System.IO.Stream stream = cce.NonNull( System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("DafnyPrelude.bpl")) // Use this once Spec#/VSIP supports designating a non-.resx project item as an embedded resource
string codebase = cce.NonNull( System.IO.Path.GetDirectoryName(cce.NonNull(System.Reflection.Assembly.GetExecutingAssembly().Location)));
string preludePath = System.IO.Path.Combine(codebase, "DafnyPrelude.bpl");
Bpl.Program prelude;
int errorCount = Bpl.Parser.Parse(preludePath, null, out prelude);
if (prelude == null || errorCount > 0) {
return null;
} else {
return prelude;
}
/*
List defines = new List();
using (System.IO.Stream stream = new System.IO.FileStream(preludePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
BoogiePL.Buffer.Fill(new System.IO.StreamReader(stream), defines);
//BoogiePL.Scanner.Init("");
Bpl.Program prelude;
int errorCount = BoogiePL.Parser.Parse(out prelude);
if (prelude == null || errorCount > 0) {
return null;
} else {
return prelude;
}
}
*/
}
public Bpl.Program Translate(Program program) {
Contract.Requires(program != null);
Contract.Ensures(Contract.Result() != null);
if (sink == null || predef == null) {
// something went wrong during construction, which reads the prelude; an error has
// already been printed, so just return an empty program here (which is non-null)
return new Bpl.Program();
}
foreach (ModuleDecl m in program.Modules) {
foreach (TopLevelDecl d in m.TopLevelDecls) {
if (d is DatatypeDecl) {
AddDatatype((DatatypeDecl)d);
} else {
AddClassMembers((ClassDecl)d);
}
}
}
return sink;
}
void AddDatatype(DatatypeDecl dt)
{
Contract.Requires(dt != null);
Contract.Requires( sink != null && predef != null);
foreach (DatatypeCtor ctor in dt.Ctors) {
// Add: function #dt.ctor(paramTypes) returns (DatatypeType);
Bpl.VariableSeq argTypes = new Bpl.VariableSeq();
foreach (Formal arg in ctor.Formals) {
Bpl.Variable a = new Bpl.Formal(arg.tok, new Bpl.TypedIdent(arg.tok, Bpl.TypedIdent.NoName, TrType(arg.Type)), true);
argTypes.Add(a);
}
Bpl.Variable resType = new Bpl.Formal(ctor.tok, new Bpl.TypedIdent(ctor.tok, Bpl.TypedIdent.NoName, predef.DatatypeType), false);
Bpl.Function fn = new Bpl.Function(ctor.tok, ctor.FullName, argTypes, resType);
sink.TopLevelDeclarations.Add(fn);
// Add: const unique ##dt.ctor: DtCtorId;
Bpl.Constant cid = new Bpl.Constant(ctor.tok, new Bpl.TypedIdent(ctor.tok, "#" + ctor.FullName, predef.DtCtorId), true);
sink.TopLevelDeclarations.Add(cid);
// Add: axiom (forall params :: DatatypeCtorId(#dt.ctor(params)) == ##dt.ctor);
Bpl.VariableSeq bvs;
List args;
CreateBoundVariables(ctor.Formals, out bvs, out args);
Bpl.Expr lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, lhs);
Bpl.Expr q = Bpl.Expr.Eq(lhs, new Bpl.IdentifierExpr(ctor.tok, cid));
if (bvs.Length != 0) {
q = new Bpl.ForallExpr(ctor.tok, bvs, q);
}
sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
// Add injectivity axioms:
int i = 0;
foreach (Formal arg in ctor.Formals) {
// function ##dt.ctor#i(DatatypeType) returns (Ti);
argTypes = new Bpl.VariableSeq();
argTypes.Add(new Bpl.Formal(ctor.tok, new Bpl.TypedIdent(ctor.tok, Bpl.TypedIdent.NoName, predef.DatatypeType), true));
resType = new Bpl.Formal(arg.tok, new Bpl.TypedIdent(arg.tok, Bpl.TypedIdent.NoName, TrType(arg.Type)), false);
fn = new Bpl.Function(ctor.tok, "#" + ctor.FullName + "#" + i, argTypes, resType);
sink.TopLevelDeclarations.Add(fn);
// axiom (forall params :: ##dt.ctor#i(#dt.ctor(params)) == params_i);
CreateBoundVariables(ctor.Formals, out bvs, out args);
lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
lhs = FunctionCall(ctor.tok, fn.Name, TrType(arg.Type), lhs);
q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Eq(lhs, args[i]));
sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
if (arg.Type.IsDatatype || arg.Type.IsTypeParameter) {
// for datatype: axiom (forall params :: DtRank(params_i) < DtRank(#dt.ctor(params)));
// for type-parameter type: axiom (forall params :: DtRank(Unbox(params_i)) < DtRank(#dt.ctor(params)));
CreateBoundVariables(ctor.Formals, out bvs, out args);
lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null,
arg.Type.IsDatatype ? args[i] : FunctionCall(ctor.tok, BuiltinFunction.Unbox, predef.DatatypeType, args[i]));
Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Lt(lhs, rhs));
sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
} else if (arg.Type is SeqType) {
// axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params)));
// that is:
// axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(Unbox(Seq#Index(arg,i))) < DtRank(#dt.ctor(params)));
CreateBoundVariables(ctor.Formals, out bvs, out args);
Bpl.Variable iVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "i", Bpl.Type.Int));
bvs.Add(iVar);
Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, iVar);
Bpl.Expr ante = Bpl.Expr.And(
Bpl.Expr.Le(Bpl.Expr.Literal(0), ie),
Bpl.Expr.Lt(ie, FunctionCall(arg.tok, BuiltinFunction.SeqLength, null, args[i])));
lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null,
FunctionCall(arg.tok, BuiltinFunction.Unbox, predef.DatatypeType,
FunctionCall(arg.tok, BuiltinFunction.SeqIndex, predef.DatatypeType, args[i], ie)));
Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
} else if (arg.Type is SetType) {
// axiom (forall params, d: Datatype :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params)));
// that is:
// axiom (forall params, d: Datatype :: arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params)));
CreateBoundVariables(ctor.Formals, out bvs, out args);
Bpl.Variable dVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "d", predef.DatatypeType));
bvs.Add(dVar);
Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, dVar);
Bpl.Expr ante = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie));
lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie);
Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args);
rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs);
q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs)));
sink.TopLevelDeclarations.Add(new Bpl.Axiom(ctor.tok, q));
}
i++;
}
}
}
void CreateBoundVariables(List/*!*/ formals, out Bpl.VariableSeq/*!*/ bvs, out List/*!*/ args)
{
Contract.Requires(formals != null);
Contract.Ensures( Contract.ValueAtReturn(out bvs).Length == Contract.ValueAtReturn(out args).Count);
Contract.Ensures(Contract.ValueAtReturn(out bvs) != null);
Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out args)));
bvs = new Bpl.VariableSeq();
args = new List();
foreach (Formal arg in formals) {
Contract.Assert(arg != null);
Bpl.Variable bv = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "a" + bvs.Length, TrType(arg.Type)));
bvs.Add(bv);
args.Add(new Bpl.IdentifierExpr(arg.tok, bv));
}
}
void AddClassMembers(ClassDecl c)
{
Contract.Requires( sink != null && predef != null);
Contract.Requires(c != null);
sink.TopLevelDeclarations.Add(GetClass(c));
foreach (MemberDecl member in c.Members) {
if (member is Field) {
Field f = (Field)member;
Bpl.Constant fc = GetField(f);
sink.TopLevelDeclarations.Add(fc);
AddAllocationAxiom(f);
} else if (member is Function) {
Function f = (Function)member;
AddFunction(f);
if (f.Body != null) {
if (f.Body is MatchExpr) {
MatchExpr me = (MatchExpr)f.Body;
Formal formal = (Formal)((IdentifierExpr)me.Source).Var; // correctness of casts follows from what resolution checks
foreach (MatchCaseExpr mc in me.Cases) {
Contract.Assert( mc.Ctor != null); // the field is filled in by resolution
Bpl.Axiom ax = FunctionAxiom(f, mc.Body, formal, mc.Ctor, mc.Arguments);
sink.TopLevelDeclarations.Add(ax);
}
} else {
Bpl.Axiom ax = FunctionAxiom(f, f.Body, null, null, null);
sink.TopLevelDeclarations.Add(ax);
}
if (f.IsRecursive && !f.IsUnlimited) {
AddLimitedAxioms(f);
}
}
AddFrameAxiom(f);
AddWellformednessCheck(f);
} else if (member is Method) {
Method m = (Method)member;
// wellformedness check for method specification
Bpl.Procedure proc = AddMethod(m, true, false);
sink.TopLevelDeclarations.Add(proc);
AddMethodImpl(m, proc, true);
// the method itself
proc = AddMethod(m, false, false);
sink.TopLevelDeclarations.Add(proc);
if (m.Body != null) {
// ...and its implementation
AddMethodImpl(m, proc, false);
}
// refinement condition
if (member is MethodRefinement) {
AddMethodRefinement((MethodRefinement)member);
}
} else if (member is CouplingInvariant) {
// TODO: define a well-foundedness condition to check
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member
}
}
}
Bpl.Axiom/*!*/ FunctionAxiom(Function/*!*/ f, Expression/*!*/ body, Formal specializationFormal,
DatatypeCtor ctor, List specializationReplacementFormals){
Contract.Requires(f != null);
Contract.Requires(body != null);
Contract.Requires(cce.NonNullElements(specializationReplacementFormals));
Contract.Requires( predef != null);
Contract.Requires( specializationFormal == null && ctor == null);
Contract.Requires( specializationFormal == null && specializationReplacementFormals == null);
Contract.Requires( f.EnclosingClass != null);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
// axiom
// mh < ModuleContextHeight || (mh == ModuleContextHeight && (fh < FunctionContextHeight || InMethodContext))
// ==>
// (forall $Heap, formals :: this != null && $IsHeap($Heap) && Pre($Heap,args) ==> f(args) == body);
//
// The variables "formals" are the formals of function "f"; except, if a specialization is provided, then
// "specializationFormal" (which is expected to be among the formals of "f") is excluded and replaced by
// "specializationReplacementFormals".
// The list "args" is the list of formals of function "f"; except, if a specialization is provided, then
// "specializationFormal" is replaced by the expression "ctor(specializationReplacementFormals)".
// If a specialization is provided, occurrences of "specializationFormal" in "body" are also replaced by
// that expression.
//
// The translation of "body" uses the #limited form whenever the callee is in the same SCC of the call graph.
//
// Note, an antecedent $Heap[this,alloc] is intentionally left out: including it would only weaken
// the axiom. Moreover, leaving it out does not introduce any soundness problem, because the Dafny
// allocation statement changes only an allocation bit and then re-assumes $IsGoodHeap; so if it is
// sound after that, then it would also have been sound just before the allocation.
//
Bpl.VariableSeq formals = new Bpl.VariableSeq();
Bpl.ExprSeq args = new Bpl.ExprSeq();
Bpl.BoundVariable bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType));
formals.Add(bv);
args.Add(new Bpl.IdentifierExpr(f.tok, bv));
Bpl.BoundVariable bvThis;
Bpl.Expr bvThisIdExpr;
if (f.IsStatic) {
bvThis = null;
bvThisIdExpr = null;
} else {
bvThis = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
formals.Add(bvThis);
bvThisIdExpr = new Bpl.IdentifierExpr(f.tok, bvThis);
args.Add(bvThisIdExpr);
}
DatatypeValue r = null;
if (specializationReplacementFormals != null) {
Contract.Assert( ctor != null); // follows from if guard and the precondition
List rArgs = new List();
foreach (BoundVar p in specializationReplacementFormals) {
bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
formals.Add(bv);
IdentifierExpr ie = new IdentifierExpr(p.tok, p.UniqueName);
ie.Var = p; ie.Type = ie.Var.Type; // resolve it here
rArgs.Add(ie);
}
r = new DatatypeValue(f.tok, cce.NonNull(ctor.EnclosingDatatype).Name, ctor.Name, rArgs);
r.Ctor = ctor; r.Type = new UserDefinedType(f.tok, ctor.EnclosingDatatype.Name, new List()/*this is not right, but it seems like it won't matter here*/); // resolve it here
}
foreach (Formal p in f.Formals) {
if (p != specializationFormal) {
bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
formals.Add(bv);
args.Add(new Bpl.IdentifierExpr(p.tok, bv));
} else {
Contract.Assert( r != null); // it is set above
args.Add(etran.TrExpr(r));
}
}
// mh < ModuleContextHeight || (mh == ModuleContextHeight && (fh < FunctionContextHeight || InMethodContext))
ModuleDecl m = f.EnclosingClass.Module;
Bpl.Expr activate = Bpl.Expr.Or(
Bpl.Expr.Lt(Bpl.Expr.Literal(m.Height), etran.ModuleContextHeight()),
Bpl.Expr.And(
Bpl.Expr.Eq(Bpl.Expr.Literal(m.Height), etran.ModuleContextHeight()),
Bpl.Expr.Or(
Bpl.Expr.Lt(Bpl.Expr.Literal(m.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight()),
etran.InMethodContext())));
Bpl.Expr ante = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr);
if (!f.IsStatic) {
Contract.Assert( bvThisIdExpr != null); // set to non-null value above when !f.IsStatic
ante = Bpl.Expr.And(ante, Bpl.Expr.Neq(bvThisIdExpr, predef.Null));
}
foreach (Expression req in f.Req) {
ante = Bpl.Expr.And(ante, etran.TrExpr(req));
}
Bpl.FunctionCall funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, funcID, args);
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(funcAppl));
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
if (specializationFormal != null) {
Contract.Assert( r != null);
Dictionary substMap = new Dictionary();
substMap.Add(specializationFormal, r);
body = Substitute(body, null, substMap);
}
Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, Bpl.Expr.Eq(funcAppl, etran.LimitedFunctions(f).TrExpr(body))));
return new Bpl.Axiom(f.tok, Bpl.Expr.Imp(activate, ax));
}
void AddLimitedAxioms(Function f){
Contract.Requires(f != null);
Contract.Requires( f.IsRecursive && !f.IsUnlimited);
Contract.Requires( sink != null && predef != null);
// axiom (forall formals :: { f(args) } f(args) == f#limited(args))
Bpl.VariableSeq formals = new Bpl.VariableSeq();
Bpl.ExprSeq args = new Bpl.ExprSeq();
Bpl.BoundVariable bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType));
formals.Add(bv);
args.Add(new Bpl.IdentifierExpr(f.tok, bv));
Bpl.BoundVariable bvThis;
Bpl.Expr bvThisIdExpr;
if (f.IsStatic) {
bvThis = null;
bvThisIdExpr = null;
} else {
bvThis = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
formals.Add(bvThis);
bvThisIdExpr = new Bpl.IdentifierExpr(f.tok, bvThis);
args.Add(bvThisIdExpr);
}
foreach (Formal p in f.Formals) {
bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
formals.Add(bv);
args.Add(new Bpl.IdentifierExpr(p.tok, bv));
}
Bpl.FunctionCall origFuncID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
Bpl.Expr origFuncAppl = new Bpl.NAryExpr(f.tok, origFuncID, args);
Bpl.FunctionCall limitedFuncID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName + "#limited", TrType(f.ResultType)));
Bpl.Expr limitedFuncAppl = new Bpl.NAryExpr(f.tok, limitedFuncID, args);
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(origFuncAppl));
Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Eq(origFuncAppl, limitedFuncAppl));
sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
}
void AddAllocationAxiom(Field f)
{
Contract.Requires(f != null);
Contract.Requires( sink != null && predef != null);
if (f.Type is BoolType || f.Type is IntType || f.Type.IsTypeParameter) {
return;
}
Bpl.BoundVariable hVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h", predef.HeapType));
Bpl.Expr h = new Bpl.IdentifierExpr(f.tok, hVar);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, h);
Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$o", predef.RefType));
Bpl.Expr o = new Bpl.IdentifierExpr(f.tok, oVar);
// h[o,f]
Bpl.Expr oDotF = Bpl.Expr.SelectTok(f.tok, h, o, new Bpl.IdentifierExpr(f.tok, GetField(f)));
// $IsGoodHeap(h) && o != null && h[o,alloc]
Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.And(
FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, h),
Bpl.Expr.Neq(o, predef.Null)),
etran.IsAlloced(f.tok, o));
if (f.Type is SetType) {
SetType st = (SetType)f.Type;
if (st.Arg.IsRefType) {
// axiom (forall h: [ref, Field x]x, o: ref, t: BoxType ::
// { h[o,f][t] }
// $IsGoodHeap(h) && o != null && h[o,alloc] && h[o,f][t] ==> UnBox(t) == null || h[UnBox(t), alloc]);
Bpl.BoundVariable tVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$t", predef.BoxType));
Bpl.Expr t = new Bpl.IdentifierExpr(f.tok, tVar);
Bpl.Expr oDotFsubT = Bpl.Expr.SelectTok(f.tok, oDotF, t);
Bpl.Expr unboxT = FunctionCall(f.tok, BuiltinFunction.Unbox, predef.RefType, t);
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotFsubT));
Bpl.Expr goodRef = etran.GoodRef(f.tok, unboxT, st.Arg);
Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(ante, oDotFsubT), Bpl.Expr.Or(Bpl.Expr.Eq(unboxT, predef.Null), goodRef));
Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar, tVar), tr, body);
sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
} else {
// TODO: should also handle sets of sets, etc. This will probably require some extra predicates, and these predicates may even replace the above treatment of sets.
}
} else if (f.Type is SeqType) {
SeqType st = (SeqType)f.Type;
if (st.Arg.IsRefType) {
// axiom (forall h: [ref, Field x]x, o: ref, i: int ::
// { Seq#Index(h[o,f], i) }
// $IsGoodHeap(h) && o != null && h[o,alloc] && 0 <= i && i < Seq#Length(h[o,f]) ==> Unbox(Seq#Index(h[o,f], i)) == null || h[Unbox(Seq#Index(h[o,f], i)), alloc]);
Bpl.BoundVariable iVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$i", Bpl.Type.Int));
Bpl.Expr i = new Bpl.IdentifierExpr(f.tok, iVar);
Bpl.Expr oDotFsubI = FunctionCall(f.tok, BuiltinFunction.SeqIndex, predef.BoxType, oDotF, i);
Bpl.Expr unbox = FunctionCall(f.tok, BuiltinFunction.Unbox, predef.RefType, oDotFsubI);
Bpl.Expr range = InSeqRange(f.tok, i, oDotF, true, null, false);
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotFsubI));
Bpl.Expr goodRef = etran.GoodRef(f.tok, unbox, st.Arg);
Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(ante, range), Bpl.Expr.Or(Bpl.Expr.Eq(unbox, predef.Null), goodRef));
Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar, iVar), tr, body);
sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
} else {
// TODO: should also handle sequences of sequences, etc. This will probably require some extra predicates, and these predicates may even replace the above treatment of sequences.
}
} else if (f.Type.IsDatatype) {
// TODO
} else {
// reference type:
// axiom (forall h: [ref, Field x]x, o: ref ::
// { h[o,f] }
// $IsGoodHeap(h) && o != null && h[o,alloc] ==> h[o,f] == null || h[h[o,f], alloc]);
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotF));
Bpl.Expr goodRef = etran.GoodRef(f.tok, oDotF, f.Type);
Bpl.Expr body = Bpl.Expr.Imp(ante, Bpl.Expr.Or(Bpl.Expr.Eq(oDotF, predef.Null), goodRef));
Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar), tr, body);
sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
}
}
Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Bpl.Expr seq, bool isSequence, Bpl.Expr lowerBound, bool includeUpperBound) {
Contract.Requires(tok != null);
Contract.Requires(index != null);
Contract.Requires(seq != null);
Contract.Ensures(Contract.Result() != null);
if (lowerBound == null) {
lowerBound = Bpl.Expr.Literal(0);
}
Bpl.Expr lower = Bpl.Expr.Le(lowerBound, index);
Bpl.Expr length = FunctionCall(tok, isSequence ? BuiltinFunction.SeqLength : BuiltinFunction.ArrayLength, null, seq);
Bpl.Expr upper;
if (includeUpperBound) {
upper = Bpl.Expr.Le(index, length);
} else {
upper = Bpl.Expr.Lt(index, length);
}
return Bpl.Expr.And(lower, upper);
}
Method currentMethod = null; // the method whose implementation is currently being translated
int loopHeapVarCount = 0;
int otherTmpVarCount = 0;
Bpl.IdentifierExpr _phvie = null;
Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, Bpl.VariableSeq locals){ // local variable that's shared between statements that need it
Contract.Requires(tok != null);
Contract.Requires(locals != null); Contract.Requires( predef != null);
Contract.Ensures(Contract.Result() != null);
if (_phvie == null) {
// the "tok" of the first request for this variable is the one we use
Bpl.LocalVariable prevHeapVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$prevHeap", predef.HeapType));
locals.Add(prevHeapVar);
_phvie = new Bpl.IdentifierExpr(tok, prevHeapVar);
}
return _phvie;
}
Bpl.IdentifierExpr _nwie = null;
Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, Bpl.VariableSeq locals) // local variable that's shared between statements that need it
{ Contract.Requires(tok != null);
Contract.Requires(locals != null);
Contract.Requires( predef != null);
Contract.Ensures(Contract.Result() != null);
if (_nwie == null) {
// the "tok" of the first request for this variable is the one we use
Bpl.LocalVariable nwVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$nw", predef.RefType)); // important: no where clause (that's why we're going through the trouble of setting of this variable in the first place)
locals.Add(nwVar);
_nwie = new Bpl.IdentifierExpr(tok, nwVar);
}
return _nwie;
}
void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc)
{
Contract.Requires(m != null);
Contract.Requires(proc != null);
Contract.Requires( sink != null && predef != null);
Contract.Requires( wellformednessProc || m.Body != null);
Contract.Requires( currentMethod == null && loopHeapVarCount == 0 && _phvie == null && _nwie == null);
Contract.Ensures( currentMethod == null && loopHeapVarCount == 0 && _phvie == null && _nwie == null);
currentMethod = m;
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
Bpl.VariableSeq inParams = Bpl.Formal.StripWhereClauses(proc.InParams);
Bpl.VariableSeq outParams = Bpl.Formal.StripWhereClauses(proc.OutParams);
Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
GenerateImplPrelude(m, inParams, outParams, builder, localVariables);
Bpl.StmtList stmts;
if (!wellformednessProc) {
// translate the body of the method
Contract.Assert( m.Body != null); // follows from method precondition and the if guard
stmts = TrStmt2StmtList(builder, m.Body, localVariables, etran);
} else {
// check well-formedness of the preconditions, and then assume each one of them
foreach (MaybeFreeExpression p in m.Req) {
CheckWellformed(p.E, null, Position.Positive, localVariables, builder, etran);
builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
}
// Note: the modifies clauses are not checked for well-formedness (is that sound?), because it used to
// be that the syntax was not rich enough for programmers to specify modifies clauses and always being
// absolutely well-defined.
// check well-formedness of the decreases clauses
foreach (Expression p in m.Decreases) {
CheckWellformed(p, null, Position.Positive, localVariables, builder, etran);
}
// play havoc with the heap according to the modifies clause
builder.Add(new Bpl.HavocCmd(m.tok, new Bpl.IdentifierExprSeq((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr)));
// assume (forall o: ref, f: Field alpha :: { $Heap[o,f] } $_Frame[o] || $Heap[o,f] == old($Heap)[o,f]);
Bpl.TypeVariable alpha = new Bpl.TypeVariable(m.tok, "alpha");
Bpl.BoundVariable oVar = new Bpl.BoundVariable(m.tok, new Bpl.TypedIdent(m.tok, "$o", predef.RefType));
Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(m.tok, oVar);
Bpl.BoundVariable fVar = new Bpl.BoundVariable(m.tok, new Bpl.TypedIdent(m.tok, "$f", predef.FieldName(m.tok, alpha)));
Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(m.tok, fVar);
Bpl.Expr heapOF = Bpl.Expr.SelectTok(m.tok, etran.HeapExpr, o, f);
Bpl.Expr oldHeapOF = Bpl.Expr.SelectTok(m.tok, etran.Old.HeapExpr, o, f);
Bpl.Expr inEnclosingFrame = Bpl.Expr.Select(etran.Old.TheFrame(m.tok), o, f);
Bpl.Expr body = Bpl.Expr.Or(inEnclosingFrame, Bpl.Expr.Eq(heapOF, oldHeapOF));
Bpl.Trigger tr = new Bpl.Trigger(m.tok, true, new Bpl.ExprSeq(heapOF));
Bpl.Expr qq = new Bpl.ForallExpr(m.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null, tr, body);
builder.Add(new Bpl.AssumeCmd(m.tok, qq));
// also play havoc with the out parameters
if (outParams.Length != 0) { // don't create an empty havoc statement
Bpl.IdentifierExprSeq outH = new Bpl.IdentifierExprSeq();
foreach (Bpl.Variable b in outParams) {
Contract.Assert(b != null);
outH.Add(new Bpl.IdentifierExpr(b.tok, b));
}
builder.Add(new Bpl.HavocCmd(m.tok, outH));
}
// check wellformedness of postconditions
foreach (MaybeFreeExpression p in m.Ens) {
CheckWellformed(p.E, null, Position.Positive, localVariables, builder, etran);
builder.Add(new Bpl.AssumeCmd(p.E.tok, etran.TrExpr(p.E)));
}
stmts = builder.Collect(m.tok);
}
Bpl.Implementation impl = new Bpl.Implementation(m.tok, proc.Name,
typeParams, inParams, outParams,
localVariables, stmts);
sink.TopLevelDeclarations.Add(impl);
currentMethod = null;
loopHeapVarCount = 0;
otherTmpVarCount = 0;
_phvie = null;
_nwie = null;
}
void GenerateImplPrelude(Method m, Bpl.VariableSeq inParams, Bpl.VariableSeq outParams,
Bpl.StmtListBuilder builder, Bpl.VariableSeq localVariables){
Contract.Requires(m != null);
Contract.Requires(inParams != null);
Contract.Requires(outParams != null);
Contract.Requires(builder != null);
Contract.Requires(localVariables != null);
Contract.Requires( predef != null);
// Add CEV prelude
CEVPrelude(m, inParams, outParams, builder);
// set up the information used to verify the method's modifies clause
DefineFrame(m.tok, m.Mod, builder, localVariables);
}
void CEVPrelude(Method m, Bpl.VariableSeq inParams, Bpl.VariableSeq outParams, Bpl.StmtListBuilder builder)
{
Contract.Requires(m != null);
Contract.Requires(inParams != null);
Contract.Requires(outParams != null);
Contract.Requires(builder != null);
Contract.Requires( predef != null);
builder.Add(Bpl.Cmd.SimpleAssign(m.tok, Bpl.Expr.Ident("#cev_pc", Bpl.Type.Int), Bpl.Expr.Add(Bpl.Expr.Ident("#cev_pc", Bpl.Type.Int), Bpl.Expr.Literal(1))));
foreach (Bpl.Variable p in inParams) {
Contract.Assert( p != null);
builder.Add(new Bpl.CallCmd(p.tok, "CevVarIntro",
new Bpl.ExprSeq(
CevLocation(p.tok),
new Bpl.IdentifierExpr(p.tok, "cev_parameter", predef.CevVariableKind),
CevVariable(p.tok, p.Name),
new Bpl.IdentifierExpr(p.tok, p.Name, p.TypedIdent.Type)), // it's important not to pass in just p, because that resolves to the proc's param, not the impl's param
new Bpl.IdentifierExprSeq()));
}
foreach (Bpl.Variable p in outParams) {
Contract.Assert( p != null);
builder.Add(new Bpl.CallCmd(p.tok, "CevVarIntro",
new Bpl.ExprSeq(
CevLocation(p.tok),
new Bpl.IdentifierExpr(p.tok, "cev_local", predef.CevVariableKind), // treat out-parameters as locals
CevVariable(p.tok, p.Name),
new Bpl.IdentifierExpr(p.tok, p.Name, p.TypedIdent.Type)), // it's important not to pass in just p, because that resolves to the proc's param, not the impl's param
new Bpl.IdentifierExprSeq()));
}
}
void DefineFrame(IToken/*!*/ tok, List/*!*/ frameClause, Bpl.StmtListBuilder/*!*/ builder, Bpl.VariableSeq/*!*/ localVariables){
Contract.Requires(tok != null);
Contract.Requires(cce.NonNullElements(frameClause));
Contract.Requires(builder != null);
Contract.Requires(cce.NonNullElements(localVariables));
Contract.Requires( predef != null);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, tok);
// Declare a local variable $_Frame: [ref, Field alpha]bool
Bpl.IdentifierExpr theFrame = etran.TheFrame(tok); // this is a throw-away expression, used only to extract the name and type of the $_Frame variable
Contract.Assert( theFrame.Type != null); // follows from the postcondition of TheFrame
Bpl.LocalVariable frame = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, theFrame.Name, theFrame.Type));
localVariables.Add(frame);
// $_Frame := (lambda $o: ref, $f: Field alpha :: $o != null && $Heap[$o,alloc] ==> ($o,$f) in Modifies/Reads-Clause);
Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.IsAlloced(tok, o));
Bpl.Expr consequent = InRWClause(tok, o, f, frameClause, etran, null, null);
Bpl.Expr lambda = new Bpl.LambdaExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null,
Bpl.Expr.Imp(ante, consequent));
builder.Add(Bpl.Cmd.SimpleAssign(tok, new Bpl.IdentifierExpr(tok, frame), lambda));
}
void CheckFrameSubset(IToken tok, List/*!*/ calleeFrame,
Expression receiverReplacement, Dictionary substMap,
ExpressionTranslator/*!*/ etran, Bpl.StmtListBuilder/*!*/ builder, string errorMessage)
{
Contract.Requires(tok != null);
Contract.Requires(cce.NonNullElements(calleeFrame));
Contract.Requires(receiverReplacement != null);
Contract.Requires(cce.NonNullElements(substMap));
Contract.Requires(etran != null);
Contract.Requires(builder != null);
Contract.Requires(errorMessage != null);
Contract.Requires( predef != null);
// emit: assert (forall o: ref, f: Field alpha :: o != null && $Heap[o,alloc] && (o,f) in calleeFrame ==> $_Frame[o,f]);
Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.IsAlloced(tok, o));
Bpl.Expr oInCallee = InRWClause(tok, o, f, calleeFrame, etran, receiverReplacement, substMap);
Bpl.Expr inEnclosingFrame = Bpl.Expr.Select(etran.TheFrame(tok), o, f);
Bpl.Expr q = new Bpl.ForallExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar),
Bpl.Expr.Imp(Bpl.Expr.And(ante, oInCallee), inEnclosingFrame));
builder.Add(Assert(tok, q, errorMessage));
}
///
/// Generates:
/// axiom (forall h0: HeapType, h1: HeapType, formals... ::
/// { HeapSucc(h0,h1), F(h1,formals) }
/// heaps are well-formed and formals are allocated AND
/// HeapSucc(h0,h1)
/// AND
/// (forall(alpha) o: ref, f: Field alpha ::
/// o != null AND h0[o,alloc] AND h1[o,alloc] AND
/// o in reads clause of formals in h0
/// IMPLIES h0[o,f] == h1[o,f])
/// IMPLIES
/// F(h0,formals) == F(h1,formals)
/// );
///
/// If the function is a recursive, non-unlimited function, then the same axiom is also produced for "F#limited" instead of "F".
///
void AddFrameAxiom(Function f)
{
Contract.Requires(f != null);
Contract.Requires( sink != null && predef != null);
Bpl.BoundVariable h0Var = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h0", predef.HeapType));
Bpl.BoundVariable h1Var = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$h1", predef.HeapType));
Bpl.Expr h0 = new Bpl.IdentifierExpr(f.tok, h0Var);
Bpl.Expr h1 = new Bpl.IdentifierExpr(f.tok, h1Var);
ExpressionTranslator etran0 = new ExpressionTranslator(this, predef, h0);
ExpressionTranslator etran1 = new ExpressionTranslator(this, predef, h1);
Bpl.Expr wellFormed = Bpl.Expr.And(
FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran0.HeapExpr),
FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran1.HeapExpr));
Bpl.TypeVariable alpha = new Bpl.TypeVariable(f.tok, "alpha");
Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$o", predef.RefType));
Bpl.Expr o = new Bpl.IdentifierExpr(f.tok, oVar);
Bpl.BoundVariable fieldVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$f", predef.FieldName(f.tok, alpha)));
Bpl.Expr field = new Bpl.IdentifierExpr(f.tok, fieldVar);
Bpl.Expr oNotNull = Bpl.Expr.Neq(o, predef.Null);
Bpl.Expr oNotNullAlloced = Bpl.Expr.And(oNotNull, Bpl.Expr.And(etran0.IsAlloced(f.tok, o), etran1.IsAlloced(f.tok, o)));
Bpl.Expr unchanged = Bpl.Expr.Eq(Bpl.Expr.SelectTok(f.tok, h0, o, field), Bpl.Expr.SelectTok(f.tok, h1, o, field));
Bpl.Expr heapSucc = FunctionCall(f.tok, BuiltinFunction.HeapSucc, null, h0, h1);
Bpl.Expr r0 = InRWClause(f.tok, o, field, f.Reads, etran0, null, null);
Bpl.Expr q0 = new Bpl.ForallExpr(f.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fieldVar),
Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced, r0), unchanged));
// bvars: h0, h1, formals
// f0args: h0, formals
// f1args: h1, formals
Bpl.VariableSeq bvars = new Bpl.VariableSeq();
Bpl.ExprSeq f0args = new Bpl.ExprSeq();
Bpl.ExprSeq f1args = new Bpl.ExprSeq();
bvars.Add(h0Var); bvars.Add(h1Var);
f0args.Add(h0);
f1args.Add(h1);
if (!f.IsStatic) {
Bpl.BoundVariable thVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
Bpl.Expr th = new Bpl.IdentifierExpr(f.tok, thVar);
bvars.Add(thVar);
f0args.Add(th);
f1args.Add(th);
Type thisType = Resolver.GetThisType(f.tok, cce.NonNull(f.EnclosingClass));
Bpl.Expr wh = Bpl.Expr.And(Bpl.Expr.Neq(th, predef.Null),
Bpl.Expr.And(etran0.GoodRef(f.tok, th, thisType), etran1.GoodRef(f.tok, th, thisType)));
wellFormed = Bpl.Expr.And(wellFormed, wh);
}
foreach (Formal p in f.Formals) {
Bpl.BoundVariable bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
bvars.Add(bv);
Bpl.Expr formal = new Bpl.IdentifierExpr(p.tok, bv);
f0args.Add(formal);
f1args.Add(formal);
Bpl.Expr wh = GetWhereClause(p.tok, formal, p.Type, etran0);
if (wh != null) { wellFormed = Bpl.Expr.And(wellFormed, wh); }
wh = GetWhereClause(p.tok, formal, p.Type, etran1);
if (wh != null) { wellFormed = Bpl.Expr.And(wellFormed, wh); }
}
string axiomComment = "frame axiom for " + f.FullName;
Bpl.FunctionCall fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
while (fn != null) {
Bpl.Expr F0 = new Bpl.NAryExpr(f.tok, fn, f0args);
Bpl.Expr F1 = new Bpl.NAryExpr(f.tok, fn, f1args);
Bpl.Expr eq = Bpl.Expr.Eq(F0, F1);
Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(heapSucc, F1));
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, bvars, null, tr,
Bpl.Expr.Imp(Bpl.Expr.And(wellFormed, heapSucc),
Bpl.Expr.Imp(q0, eq)));
sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax, axiomComment));
if (axiomComment != null && f.IsRecursive && !f.IsUnlimited) {
fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName + "#limited", TrType(f.ResultType)));
axiomComment = null; // the comment goes only with the first frame axiom
} else {
break; // no more frame axioms to produce
}
}
}
Bpl.Expr/*!*/ InRWClause(IToken/*!*/ tok, Bpl.Expr/*!*/ o, Bpl.Expr/*!*/ f, List/*!*/ rw, ExpressionTranslator/*!*/ etran,
Expression receiverReplacement, Dictionary substMap){
Contract.Requires(tok != null);
Contract.Requires(o != null);
Contract.Requires(f != null);
Contract.Requires(etran != null);
Contract.Requires(cce.NonNullElements(rw));
Contract.Requires(cce.NonNullElements(substMap));
Contract.Requires( predef != null);Contract.Requires( receiverReplacement == null && substMap == null);
Contract.Ensures(Contract.Result() != null);
// requires o to denote an expression of type RefType
// "rw" is is allowed to contain a WildcardExpr
Bpl.Expr disjunction = null;
foreach (FrameExpression rwComponent in rw) {
Expression e = rwComponent.E;
if (substMap != null) {
Contract.Assert( receiverReplacement != null);
e = Substitute(e, receiverReplacement, substMap);
}
Bpl.Expr disjunct;
if (e is WildcardExpr) {
disjunct = Bpl.Expr.True;
} else if (e.Type is SetType) {
// old(e)[Box(o)]
disjunct = etran.TrInSet(tok, o, e, ((SetType)e.Type).Arg);
} else if (e.Type is SeqType) {
// (exists i: int :: 0 <= i && i < Seq#Length(old(e)) && Seq#Index(old(e),i) == Box(o))
Bpl.Expr boxO = FunctionCall(tok, BuiltinFunction.Box, null, o);
Bpl.Variable iVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
Bpl.Expr i = new Bpl.IdentifierExpr(tok, iVar);
Bpl.Expr iBounds = InSeqRange(tok, i, etran.TrExpr(e), true, null, false);
Bpl.Expr XsubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.BoxType, etran.TrExpr(e), i);
// TODO: the equality in the next line should be changed to one that understands extensionality
disjunct = new Bpl.ExistsExpr(tok, new Bpl.VariableSeq(iVar), Bpl.Expr.And(iBounds, Bpl.Expr.Eq(XsubI, boxO)));
} else {
// o == old(e)
disjunct = Bpl.Expr.Eq(o, etran.TrExpr(e));
}
disjunct = Bpl.Expr.And(IsTotal(e, etran), disjunct);
if (rwComponent.Field != null) {
disjunct = Bpl.Expr.And(disjunct, Bpl.Expr.Eq(f, new Bpl.IdentifierExpr(rwComponent.E.tok, GetField(rwComponent.Field))));
}
if (disjunction == null) {
disjunction = disjunct;
} else {
disjunction = Bpl.Expr.Or(disjunction, disjunct);
}
}
if (disjunction == null) {
return Bpl.Expr.False;
} else {
return disjunction;
}
}
void AddWellformednessCheck(Function f){
Contract.Requires(f != null);
Contract.Requires( sink != null && predef != null);
Contract.Requires( f.EnclosingClass != null);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
// parameters of the procedure
Bpl.VariableSeq inParams = new Bpl.VariableSeq();
if (!f.IsStatic) {
Bpl.Expr wh = Bpl.Expr.And(
Bpl.Expr.Neq(new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), predef.Null),
etran.GoodRef(f.tok, new Bpl.IdentifierExpr(f.tok, "this", predef.RefType), Resolver.GetThisType(f.tok, f.EnclosingClass)));
Bpl.Formal thVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType, wh), true);
inParams.Add(thVar);
}
foreach (Formal p in f.Formals) {
Bpl.Type varType = TrType(p.Type);
Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
}
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
// the procedure itself
Bpl.RequiresSeq req = new Bpl.RequiresSeq();
// free requires mh < ModuleContextHeight || (mh == ModuleContextHeight && fh < FunctionContextHeight);
ModuleDecl m = f.EnclosingClass.Module;
Bpl.Expr context = Bpl.Expr.And(
Bpl.Expr.Eq(Bpl.Expr.Literal(m.Height), etran.ModuleContextHeight()),
Bpl.Expr.Eq(Bpl.Expr.Literal(m.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight()));
req.Add(Requires(f.tok, true, context, null, null));
Bpl.Procedure proc = new Bpl.Procedure(f.tok, "CheckWellformed$$" + f.FullName, typeParams, inParams, new Bpl.VariableSeq(),
req, new Bpl.IdentifierExprSeq(), new Bpl.EnsuresSeq());
sink.TopLevelDeclarations.Add(proc);
Bpl.VariableSeq locals = new Bpl.VariableSeq();
Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
// todo: include CEV information of parameters and the heap
// check well-formedness of the preconditions, and then assume each one of them
foreach (Expression p in f.Req) {
// Note, in this well-formedness check, function is passed in as null. This will cause there to be
// no reads checks or decreases checks. This seems like a good idea, and I hope it is also sound.
CheckWellformed(p, null, Position.Positive, locals, builder, etran);
builder.Add(new Bpl.AssumeCmd(p.tok, etran.TrExpr(p)));
}
// Note: the reads clauses are not checked for well-formedness (is that sound?), because it used to
// be that the syntax was not rich enough for programmers to specify reads clauses and always being
// absolutely well-defined.
// check well-formedness of the decreases clauses
foreach (Expression p in f.Decreases) {
// Note, in this well-formedness check, function is passed in as null. This will cause there to be
// no reads checks or decreases checks. This seems like a good idea, and I hope it is also sound.
CheckWellformed(p, null, Position.Positive, locals, builder, etran);
}
// check well-formedness of the body
if (f.Body != null) {
DefineFrame(f.tok, f.Reads, builder, locals);
CheckWellformed(f.Body, f, Position.Positive, locals, builder, etran);
}
Bpl.Implementation impl = new Bpl.Implementation(f.tok, proc.Name,
typeParams,
Bpl.Formal.StripWhereClauses(proc.InParams),
Bpl.Formal.StripWhereClauses(proc.OutParams),
locals, builder.Collect(f.tok));
sink.TopLevelDeclarations.Add(impl);
}
Bpl.Expr IsTotal(Expression expr, ExpressionTranslator etran){
Contract.Requires(expr != null);Contract.Requires(etran != null);
Contract.Requires( predef != null);
Contract.Ensures(Contract.Result() != null);
if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr || expr is WildcardExpr) {
return Bpl.Expr.True;
} else if (expr is DisplayExpression) {
DisplayExpression e = (DisplayExpression)expr;
return IsTotal(e.Elements, etran);
} else if (expr is FieldSelectExpr) {
FieldSelectExpr e = (FieldSelectExpr)expr;
if (e.Obj is ThisExpr) {
return Bpl.Expr.True;
} else {
return Bpl.Expr.And(IsTotal(e.Obj, etran), Bpl.Expr.Neq(etran.TrExpr(e.Obj), predef.Null));
}
} else if (expr is SeqSelectExpr) {
SeqSelectExpr e = (SeqSelectExpr)expr;
bool isSequence = e.Seq.Type is SeqType;
Bpl.Expr total = IsTotal(e.Seq, etran);
Bpl.Expr seq = etran.TrExpr(e.Seq);
Bpl.Expr e0 = null;
if (e.E0 != null) {
e0 = etran.TrExpr(e.E0);
total = BplAnd(total, IsTotal(e.E0, etran));
total = BplAnd(total, InSeqRange(expr.tok, e0, seq, isSequence, null, !e.SelectOne));
}
if (e.E1 != null) {
total = BplAnd(total, IsTotal(e.E1, etran));
total = BplAnd(total, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, isSequence, e0, true));
}
return total;
} else if (expr is SeqUpdateExpr) {
SeqUpdateExpr e = (SeqUpdateExpr)expr;
Bpl.Expr total = IsTotal(e.Seq, etran);
Bpl.Expr seq = etran.TrExpr(e.Seq);
Bpl.Expr index = etran.TrExpr(e.Index);
total = BplAnd(total, IsTotal(e.Index, etran));
total = BplAnd(total, InSeqRange(expr.tok, index, seq, true, null, false));
total = BplAnd(total, IsTotal(e.Value, etran));
return total;
} else if (expr is FunctionCallExpr) {
FunctionCallExpr e = (FunctionCallExpr)expr;
Contract.Assert( e.Function != null); // follows from the fact that expr has been successfully resolved
// check well-formedness of receiver
Bpl.Expr r = IsTotal(e.Receiver, etran);
if (!e.Function.IsStatic && !(e.Receiver is ThisExpr)) {
r = BplAnd(r, Bpl.Expr.Neq(etran.TrExpr(e.Receiver), predef.Null));
}
// check well-formedness of the other parameters
r = BplAnd(r, IsTotal(e.Args, etran));
// create a substitution map from each formal parameter to the corresponding actual parameter
Dictionary substMap = new Dictionary();
for (int i = 0; i < e.Function.Formals.Count; i++) {
substMap.Add(e.Function.Formals[i], e.Args[i]);
}
// check that the preconditions for the call hold
foreach (Expression p in e.Function.Req) {
Expression precond = Substitute(p, e.Receiver, substMap);
r = BplAnd(r, etran.TrExpr(precond));
}
return r;
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
return IsTotal(dtv.Arguments, etran);
} else if (expr is OldExpr) {
OldExpr e = (OldExpr)expr;
return new Bpl.OldExpr(expr.tok, IsTotal(e.E, etran));
} else if (expr is FreshExpr) {
FreshExpr e = (FreshExpr)expr;
return IsTotal(e.E, etran);
} else if (expr is UnaryExpr) {
UnaryExpr e = (UnaryExpr)expr;
Bpl.Expr t = IsTotal(e.E, etran);
if (e.Op == UnaryExpr.Opcode.SeqLength && !(e.E.Type is SeqType)) {
return Bpl.Expr.And(t, Bpl.Expr.Neq(etran.TrExpr(e.E), predef.Null));
}
return t;
} else if (expr is BinaryExpr) {
BinaryExpr e = (BinaryExpr)expr;
Bpl.Expr t0 = IsTotal(e.E0, etran);
Bpl.Expr t1 = IsTotal(e.E1, etran);
Bpl.Expr z = null;
switch (e.ResolvedOp) {
case BinaryExpr.ResolvedOpcode.And:
case BinaryExpr.ResolvedOpcode.Imp:
t1 = Bpl.Expr.Imp(etran.TrExpr(e.E0), t1);
break;
case BinaryExpr.ResolvedOpcode.Or:
t1 = Bpl.Expr.Imp(Bpl.Expr.Not(etran.TrExpr(e.E0)), t1);
break;
case BinaryExpr.ResolvedOpcode.Div:
case BinaryExpr.ResolvedOpcode.Mod:
z = Bpl.Expr.Neq(etran.TrExpr(e.E1), Bpl.Expr.Literal(0));
break;
default:
break;
}
Bpl.Expr r = BplAnd(t0, t1);
return z == null ? r : BplAnd(r, z);
} else if (expr is QuantifierExpr) {
QuantifierExpr e = (QuantifierExpr)expr;
Bpl.Expr total = IsTotal(e.Body, etran);
if (total != Bpl.Expr.True) {
Bpl.VariableSeq bvars = new Bpl.VariableSeq();
foreach (BoundVar bv in e.BoundVars) {
bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, TrType(bv.Type))));
}
total = new Bpl.ForallExpr(expr.tok, bvars, total);
}
return total;
} else if (expr is ITEExpr) {
ITEExpr e = (ITEExpr)expr;
Bpl.Expr total = IsTotal(e.Test, etran);
Bpl.Expr test = etran.TrExpr(e.Test);
total = BplAnd(total, Bpl.Expr.Imp(test, IsTotal(e.Thn, etran)));
total = BplAnd(total, Bpl.Expr.Imp(Bpl.Expr.Not(test), IsTotal(e.Els, etran)));
return total;
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
}
}
Bpl.Expr/*!*/ IsTotal(List/*!*/ exprs, ExpressionTranslator/*!*/ etran) {
Contract.Requires(etran != null);
Contract.Requires(exprs != null);
Contract.Ensures(Contract.Result() != null);
Bpl.Expr total = Bpl.Expr.True;
foreach (Expression e in exprs) {
Contract.Assert(e != null);
total = BplAnd(total, IsTotal(e, etran));
}
return total;
}
Bpl.Expr BplAnd(Bpl.Expr a, Bpl.Expr b) {
Contract.Requires(a != null);
Contract.Requires(b != null);
Contract.Ensures(Contract.Result() != null);
if (a == Bpl.Expr.True) {
return b;
} else if (b == Bpl.Expr.True) {
return a;
} else {
return Bpl.Expr.And(a, b);
}
}
enum Position { Positive, Negative, Neither }
Position Negate(Position pos) {
switch (pos) {
case Position.Positive: return Position.Negative;
case Position.Negative: return Position.Positive;
case Position.Neither: return Position.Neither;
default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected Position
}
}
void CheckNonNull(IToken tok, Expression e, Bpl.StmtListBuilder builder, ExpressionTranslator etran){
Contract.Requires(tok != null);
Contract.Requires(e != null);
Contract.Requires(builder != null);
Contract.Requires(etran != null);
Contract.Requires( predef != null);
if (e is ThisExpr) {
// already known to be non-null
} else {
builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), "target object may be null"));
}
}
void CheckWellformed(Expression expr, Function func, Position pos, Bpl.VariableSeq locals, Bpl.StmtListBuilder builder, ExpressionTranslator etran){
Contract.Requires(expr != null);
Contract.Requires(locals != null);
Contract.Requires(builder != null);
Contract.Requires(etran != null);
Contract.Requires( predef != null);
if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr || expr is WildcardExpr) {
// always allowed
} else if (expr is DisplayExpression) {
DisplayExpression e = (DisplayExpression)expr;
foreach (Expression el in e.Elements) {
CheckWellformed(el, func, Position.Neither, locals, builder, etran);
}
} else if (expr is FieldSelectExpr) {
FieldSelectExpr e = (FieldSelectExpr)expr;
CheckWellformed(e.Obj, func, Position.Neither, locals, builder, etran);
CheckNonNull(expr.tok, e.Obj, builder, etran);
if (func != null) {
builder.Add(Assert(expr.tok, Bpl.Expr.SelectTok(expr.tok, etran.TheFrame(expr.tok), etran.TrExpr(e.Obj), GetField(e)), "insufficient reads clause to read field"));
}
} else if (expr is SeqSelectExpr) {
SeqSelectExpr e = (SeqSelectExpr)expr;
bool isSequence = e.Seq.Type is SeqType;
CheckWellformed(e.Seq, func, Position.Neither, locals, builder, etran);
Bpl.Expr seq = etran.TrExpr(e.Seq);
Bpl.Expr e0 = null;
if (e.E0 != null) {
e0 = etran.TrExpr(e.E0);
CheckWellformed(e.E0, func, Position.Neither, locals, builder, etran);
builder.Add(Assert(expr.tok, InSeqRange(expr.tok, e0, seq, isSequence, null, !e.SelectOne), "index out of range"));
}
if (e.E1 != null) {
CheckWellformed(e.E1, func, Position.Neither, locals, builder, etran);
builder.Add(Assert(expr.tok, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, isSequence, e0, true), "end-of-range beyond length of " + (isSequence ? "sequence" : "array")));
}
if (func != null && cce.NonNull(e.Seq.Type).IsArrayType) {
Contract.Assert( e.E0 != null);
Bpl.Expr fieldName = FunctionCall(expr.tok, BuiltinFunction.IndexField, null, etran.TrExpr(e.E0));
builder.Add(Assert(expr.tok, Bpl.Expr.SelectTok(expr.tok, etran.TheFrame(expr.tok), seq, fieldName), "insufficient reads clause to read array element"));
}
} else if (expr is SeqUpdateExpr) {
SeqUpdateExpr e = (SeqUpdateExpr)expr;
CheckWellformed(e.Seq, func, Position.Neither, locals, builder, etran);
Bpl.Expr seq = etran.TrExpr(e.Seq);
Bpl.Expr index = etran.TrExpr(e.Index);
CheckWellformed(e.Index, func, Position.Neither, locals, builder, etran);
builder.Add(Assert(expr.tok, InSeqRange(expr.tok, index, seq, true, null, false), "index out of range"));
CheckWellformed(e.Value, func, Position.Neither, locals, builder, etran);
} else if (expr is FunctionCallExpr) {
FunctionCallExpr e = (FunctionCallExpr)expr;
Contract.Assert( e.Function != null); // follows from the fact that expr has been successfully resolved
// check well-formedness of receiver
CheckWellformed(e.Receiver, func, Position.Neither, locals, builder, etran);
if (!e.Function.IsStatic && !(e.Receiver is ThisExpr)) {
CheckNonNull(expr.tok, e.Receiver, builder, etran);
}
// check well-formedness of the other parameters
foreach (Expression arg in e.Args) {
CheckWellformed(arg, func, Position.Neither, locals, builder, etran);
}
// create a local variable for each formal parameter, and assign each actual parameter to the corresponding local
Dictionary substMap = new Dictionary();
for (int i = 0; i < e.Function.Formals.Count; i++) {
Formal p = e.Function.Formals[i];
VarDecl local = new VarDecl(p.tok, p.Name, p.Type, p.IsGhost, null);
local.type = local.OptionalType; // resolve local here
IdentifierExpr ie = new IdentifierExpr(local.Tok, local.UniqueName);
ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here
substMap.Add(p, ie);
locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.UniqueName, TrType(local.Type))));
Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified?
Expression ee = e.Args[i];
Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, etran.CondApplyBox(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), p.Type));
builder.Add(cmd);
}
// check that the preconditions for the call hold
foreach (Expression p in e.Function.Req) {
Expression precond = Substitute(p, e.Receiver, substMap);
builder.Add(Assert(expr.tok, etran.TrExpr(precond), "possible violation of function precondition"));
}
if (func != null) {
// check that the callee reads only what the caller is already allowed to read
CheckFrameSubset(expr.tok, e.Function.Reads, e.Receiver, substMap, etran, builder, "insufficient reads clause to invoke function");
// finally, check that the decreases measure goes down
ModuleDecl module = cce.NonNull(e.Function.EnclosingClass).Module;
if (module == cce.NonNull(func.EnclosingClass).Module) {
if (module.CallGraph.GetSCCRepresentative(e.Function) == module.CallGraph.GetSCCRepresentative(func)) {
List contextDecreases = func.Decreases;
if (contextDecreases.Count == 0) {
contextDecreases = new List();
contextDecreases.Add(FrameToObjectSet(func.Reads)); // use its reads clause instead
}
List calleeDecreases = e.Function.Decreases;
if (calleeDecreases.Count == 0) {
calleeDecreases = new List();
calleeDecreases.Add(FrameToObjectSet(e.Function.Reads)); // use its reads clause instead
}
CheckCallTermination(expr.tok, contextDecreases, calleeDecreases, e.Receiver, substMap, etran, builder);
}
}
}
} else if (expr is DatatypeValue) {
DatatypeValue dtv = (DatatypeValue)expr;
foreach (Expression arg in dtv.Arguments) {
CheckWellformed(arg, func, Position.Neither, locals, builder, etran);
}
} else if (expr is OldExpr) {
OldExpr e = (OldExpr)expr;
CheckWellformed(e.E, func, pos, locals, builder, etran.Old);
} else if (expr is FreshExpr) {
FreshExpr e = (FreshExpr)expr;
CheckWellformed(e.E, func, pos, locals, builder, etran);
} else if (expr is UnaryExpr) {
UnaryExpr e = (UnaryExpr)expr;
CheckWellformed(e.E, func, e.Op == UnaryExpr.Opcode.Not ? Negate(pos) : Position.Neither, locals, builder, etran);
if (e.Op == UnaryExpr.Opcode.SeqLength && !(e.E.Type is SeqType)) {
CheckNonNull(expr.tok, e.E, builder, etran);
}
} else if (expr is BinaryExpr) {
BinaryExpr e = (BinaryExpr)expr;
CheckWellformed(e.E0, func, e.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp ? Negate(pos) : pos, locals, builder, etran);
switch (e.ResolvedOp) {
case BinaryExpr.ResolvedOpcode.And:
case BinaryExpr.ResolvedOpcode.Imp:
{
Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
CheckWellformed(e.E1, func, pos, locals, b, etran);
builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(e.E0), b.Collect(expr.tok), null, null));
}
break;
case BinaryExpr.ResolvedOpcode.Or:
{
Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
CheckWellformed(e.E1, func, pos, locals, b, etran);
builder.Add(new Bpl.IfCmd(expr.tok, Bpl.Expr.Not(etran.TrExpr(e.E0)), b.Collect(expr.tok), null, null));
}
break;
case BinaryExpr.ResolvedOpcode.Div:
case BinaryExpr.ResolvedOpcode.Mod:
CheckWellformed(e.E1, func, pos, locals, builder, etran);
builder.Add(Assert(expr.tok, Bpl.Expr.Neq(etran.TrExpr(e.E1), Bpl.Expr.Literal(0)), "possible division by zero"));
break;
default:
CheckWellformed(e.E1, func, pos, locals, builder, etran);
break;
}
} else if (expr is QuantifierExpr) {
QuantifierExpr e = (QuantifierExpr)expr;
Dictionary substMap = new Dictionary();
foreach (BoundVar bv in e.BoundVars) {
VarDecl local = new VarDecl(bv.tok, bv.Name, bv.Type, bv.IsGhost, null);
local.type = local.OptionalType; // resolve local here
IdentifierExpr ie = new IdentifierExpr(local.Tok, local.UniqueName);
ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here
substMap.Add(bv, ie);
locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.UniqueName, TrType(local.Type))));
}
Expression body = Substitute(e.Body, null, substMap);
CheckWellformed(body, func, pos, locals, builder, etran);
} else if (expr is ITEExpr) {
ITEExpr e = (ITEExpr)expr;
CheckWellformed(e.Test, func, pos, locals, builder, etran);
Bpl.StmtListBuilder bThen = new Bpl.StmtListBuilder();
Bpl.StmtListBuilder bElse = new Bpl.StmtListBuilder();
CheckWellformed(e.Thn, func, pos, locals, bThen, etran);
CheckWellformed(e.Els, func, pos, locals, bElse, etran);
builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(e.Test), bThen.Collect(expr.tok), null, bElse.Collect(expr.tok)));
} else if (expr is MatchExpr) {
MatchExpr me = (MatchExpr)expr;
CheckWellformed(me.Source, func, pos, locals, builder, etran);
Bpl.Expr src = etran.TrExpr(me.Source);
foreach (MatchCaseExpr mc in me.Cases) {
Contract.Assert( mc.Ctor != null); // follows from the fact that mc has been successfully resolved
Bpl.ExprSeq args = new Bpl.ExprSeq();
for (int i = 0; i < mc.Arguments.Count; i++) {
BoundVar p = mc.Arguments[i];
Bpl.Variable local = new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)));
locals.Add(local);
IdentifierExpr ie = new IdentifierExpr(p.tok, p.UniqueName);
ie.Var = p; ie.Type = ie.Var.Type; // resolve it here
Type t = mc.Ctor.Formals[i].Type;
args.Add(etran.CondApplyBox(expr.tok, new Bpl.IdentifierExpr(p.tok, local), cce.NonNull(p.Type), t));
}
Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(mc.tok, mc.Ctor.FullName, predef.DatatypeType);
Bpl.Expr ct = new Bpl.NAryExpr(mc.tok, new Bpl.FunctionCall(id), args);
// generate: if (src == ctor(args)) { mc.Body is well-formed }
Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
CheckWellformed(mc.Body, func, pos, locals, b, etran);
builder.Add(new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), null, null));
}
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression
}
}
Expression FrameToObjectSet(List fexprs) {
Contract.Requires(fexprs != null);
Contract.Ensures(Contract.Result() != null);
List sets = new List();
List singletons = null;
foreach (FrameExpression fe in fexprs) {
Contract.Assert(fe != null);
if (fe.E is WildcardExpr) {
// drop wildcards altogether
} else {
Expression e = fe.E; // keep only fe.E, drop any fe.Field designation
Contract.Assert( e.Type != null); // should have been resolved already
if (e.Type.IsRefType) {
// e represents a singleton set
if (singletons == null) {
singletons = new List();
}
singletons.Add(e);
} else {
// e is already a set
Contract.Assert( e.Type is SetType);
sets.Add(e);
}
}
}
if (singletons != null) {
Expression display = new SetDisplayExpr(singletons[0].tok, singletons);
display.Type = new SetType(new ObjectType()); // resolve here
sets.Add(display);
}
if (sets.Count == 0) {
Expression emptyset = new SetDisplayExpr(Token.NoToken, new List());
emptyset.Type = new SetType(new ObjectType()); // resolve here
return emptyset;
} else {
Expression s = sets[0];
for (int i = 1; i < sets.Count; i++) {
BinaryExpr union = new BinaryExpr(s.tok, BinaryExpr.Opcode.Add, s, sets[i]);
union.ResolvedOp = BinaryExpr.ResolvedOpcode.Union; // resolve here
union.Type = new SetType(new ObjectType()); // resolve here
s = union;
}
return s;
}
}
Bpl.Constant GetClass(TopLevelDecl cl)
{
Contract.Requires(cl != null);
Contract.Requires( predef != null);
Contract.Ensures(Contract.Result() != null);
Bpl.Constant cc;
if (classes.TryGetValue(cl, out cc)) {
Contract.Assert( cc != null);
} else {
cc = new Bpl.Constant(cl.tok, new Bpl.TypedIdent(cl.tok, "class." + cl.Name, predef.ClassNameType), true);
classes.Add(cl, cc);
}
return cc;
}
Bpl.Expr GetTypeExpr(IToken tok, Type type)
{
Contract.Requires(tok != null);
Contract.Requires(type != null);
Contract.Requires( predef != null);
while (true) {
TypeProxy tp = type as TypeProxy;
if (tp == null) {
break;
} else if (tp.T == null) {
// unresolved proxy
// TODO: what to do here?
return null;
} else {
type = tp.T;
}
}
if (type is BoolType) {
return new Bpl.IdentifierExpr(tok, "class.bool", predef.ClassNameType);
} else if (type is IntType) {
return new Bpl.IdentifierExpr(tok, "class.int", predef.ClassNameType);
} else if (type is ObjectType) {
return new Bpl.IdentifierExpr(tok, "class.object", predef.ClassNameType);
} else if (type is CollectionType) {
CollectionType ct = (CollectionType)type;
Bpl.Expr a = GetTypeExpr(tok, ct.Arg);
if (a == null) {
return null;
}
Bpl.Expr t = new Bpl.IdentifierExpr(tok, ct is SetType ? "class.set" : "class.seq", predef.ClassNameType);
return FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
} else {
UserDefinedType ct = (UserDefinedType)type;
if (ct.ResolvedClass == null) {
return null; // TODO: what to do here?
}
Bpl.Expr t = new Bpl.IdentifierExpr(tok, GetClass(ct.ResolvedClass));
foreach (Type arg in ct.TypeArgs) {
Bpl.Expr a = GetTypeExpr(tok, arg);
if (a == null) {
return null;
}
t = FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
}
return t;
}
}
Bpl.Constant GetField(Field f)
{
Contract.Requires(f != null);Contract.Requires( sink != null && predef != null);
Contract.Ensures(Contract.Result() != null);
Bpl.Constant fc;
if (fields.TryGetValue(f, out fc)) {
Contract.Assert( fc != null);
} else {
// const unique f: Field ty;
Bpl.Type ty = predef.FieldName(f.tok, TrType(f.Type));
fc = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, f.FullName, ty), true);
fields.Add(f, fc);
// axiom FCat(f) == $NamedField && DeclType(f) == C;
Bpl.Expr fcat = Bpl.Expr.Eq(FunctionCall(f.tok, BuiltinFunction.FCat, ty, Bpl.Expr.Ident(fc)), new Bpl.IdentifierExpr(f.tok, "$NamedField", predef.FieldCategoryType));
Bpl.Expr declType = Bpl.Expr.Eq(FunctionCall(f.tok, BuiltinFunction.DeclType, ty, Bpl.Expr.Ident(fc)), new Bpl.IdentifierExpr(f.tok, GetClass(cce.NonNull(f.EnclosingClass))));
Bpl.Axiom ax = new Bpl.Axiom(f.tok, Bpl.Expr.And(fcat, declType));
sink.TopLevelDeclarations.Add(ax);
}
return fc;
}
Bpl.Expr GetField(FieldSelectExpr fse)
{
Contract.Requires(fse != null);Contract.Requires( fse.Field != null);
Contract.Ensures(Contract.Result() != null);
return new Bpl.IdentifierExpr(fse.tok, GetField(fse.Field));
}
///
/// This method is expected to be called just once for each function in the program.
///
void AddFunction(Function f)
{
Contract.Requires(f != null);
Contract.Requires( predef != null && sink != null);
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
Bpl.VariableSeq args = new Bpl.VariableSeq();
args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$heap", predef.HeapType), true));
if (!f.IsStatic) {
args.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType), true));
}
foreach (Formal p in f.Formals) {
args.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, TrType(p.Type)), true));
}
Bpl.Formal res = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, TrType(f.ResultType)), false);
Bpl.Function func = new Bpl.Function(f.tok, f.FullName, typeParams, args, res);
sink.TopLevelDeclarations.Add(func);
if (f.IsRecursive && !f.IsUnlimited) {
Bpl.Function limitedF = new Bpl.Function(f.tok, f.FullName + "#limited", args, res);
sink.TopLevelDeclarations.Add(limitedF);
}
}
///
/// This method is expected to be called just twice for each procedure in the program (once with
/// wellformednessProc set to true, once with wellformednessProc set to false).
/// In addition, it is used once to generate refinement conditions.
///
Bpl.Procedure AddMethod(Method m, bool wellformednessProc, bool skipEnsures)
{
Contract.Requires(m != null);
Contract.Requires( predef != null);
Contract.Requires( m.EnclosingClass != null);
Contract.Requires( !skipEnsures || !wellformednessProc);
Contract.Ensures(Contract.Result() != null);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
Bpl.VariableSeq inParams = new Bpl.VariableSeq();
Bpl.VariableSeq outParams = new Bpl.VariableSeq();
if (!m.IsStatic) {
Bpl.Expr wh = Bpl.Expr.And(
Bpl.Expr.Neq(new Bpl.IdentifierExpr(m.tok, "this", predef.RefType), predef.Null),
etran.GoodRef(m.tok, new Bpl.IdentifierExpr(m.tok, "this", predef.RefType), Resolver.GetThisType(m.tok, m.EnclosingClass)));
Bpl.Formal thVar = new Bpl.Formal(m.tok, new Bpl.TypedIdent(m.tok, "this", predef.RefType, wh), true);
inParams.Add(thVar);
}
foreach (Formal p in m.Ins) {
Bpl.Type varType = TrType(p.Type);
Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), true));
}
foreach (Formal p in m.Outs) {
Bpl.Type varType = TrType(p.Type);
Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
outParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, wh), false));
}
Bpl.RequiresSeq req = new Bpl.RequiresSeq();
Bpl.IdentifierExprSeq mod = new Bpl.IdentifierExprSeq();
Bpl.EnsuresSeq ens = new Bpl.EnsuresSeq();
// free requires mh == ModuleContextHeight && InMethodContext;
Bpl.Expr context = Bpl.Expr.And(
Bpl.Expr.Eq(Bpl.Expr.Literal(m.EnclosingClass.Module.Height), etran.ModuleContextHeight()),
etran.InMethodContext());
req.Add(Requires(m.tok, true, context, null, null));
mod.Add(etran.HeapExpr);
mod.Add(Bpl.Expr.Ident("#cev_pc", Bpl.Type.Int));
if (!wellformednessProc) {
string comment = "user-defined preconditions";
foreach (MaybeFreeExpression p in m.Req) {
if (p.IsFree) {
req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment));
} else {
List definitions, pieces;
if (!SplitExpr(p.E, out definitions, out pieces)) {
req.Add(Requires(p.E.tok, false, etran.TrExpr(p.E), null, comment));
} else {
req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment)); // add the entire condition as a free precondition
Bpl.Expr ante = Bpl.Expr.True;
foreach (Expression d in definitions) {
Bpl.Expr trD = etran.TrExpr(d);
req.Add(Requires(d.tok, true, trD, null, null));
ante = Bpl.Expr.And(ante, trD);
}
foreach (Expression se in pieces) {
req.Add(Requires(se.tok, false, Bpl.Expr.Imp(ante, etran.TrExpr(se)), null, null)); // TODO: it would be fine to have these use {:subsumption 0}
}
}
}
comment = null;
}
comment = "user-defined postconditions";
if (!skipEnsures) foreach (MaybeFreeExpression p in m.Ens) {
if (p.IsFree) {
ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), null, comment));
} else {
List definitions, pieces;
if (!SplitExpr(p.E, out definitions, out pieces)) {
ens.Add(Ensures(p.E.tok, false, etran.TrExpr(p.E), null, comment));
} else {
ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), null, comment)); // add the entire condition as a free postcondition
Bpl.Expr ante = Bpl.Expr.True;
foreach (Expression d in definitions) {
Bpl.Expr trD = etran.TrExpr(d);
ens.Add(Ensures(d.tok, true, trD, null, null));
ante = Bpl.Expr.And(ante, trD);
}
foreach (Expression se in pieces) {
ens.Add(Ensures(se.tok, false, Bpl.Expr.Imp(ante, etran.TrExpr(se)), null, null)); // TODO: it would be fine to have these use {:subsumption 0}
}
}
}
comment = null;
}
if (!skipEnsures) foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m, etran.Old, etran)) {
ens.Add(Ensures(tri.tok, tri.IsFree, tri.Expr, tri.ErrorMessage, tri.Comment));
}
}
// CEV information
// free requires #cev_pc == 0;
req.Add(Requires(m.tok, true, Bpl.Expr.Eq(Bpl.Expr.Ident("#cev_pc", Bpl.Type.Int), Bpl.Expr.Literal(0)), null, "CEV information"));
// free requires #cev_init(0);
req.Add(Requires(m.tok, true, FunctionCall(m.tok, BuiltinFunction.CevInit, null, Bpl.Expr.Literal(0)), null, null));
// free requires #cev_var_intro(0, cev_implicit, #loc.$Heap, $Heap, 0);
req.Add(Requires(m.tok, true, FunctionCall(m.tok, BuiltinFunction.CevVarIntro, null,
Bpl.Expr.Literal(0),
new Bpl.IdentifierExpr(m.tok, "cev_implicit", predef.CevVariableKind),
CevVariable(m.tok, predef.HeapVarName),
etran.HeapExpr,
Bpl.Expr.Literal(0)), null, null));
// free requires #cev_save_position(0) == #tok.Tok;
req.Add(Requires(m.tok, true, Bpl.Expr.Eq(FunctionCall(m.tok, BuiltinFunction.CevProgramLocation, null, Bpl.Expr.Literal(0)), CevLocation(m.tok)), null, null));
// free ensures old(#cev_pc) < #cev_pc;
ens.Add(Ensures(m.tok, true, Bpl.Expr.Lt(new Bpl.OldExpr(m.tok, Bpl.Expr.Ident("#cev_pc", Bpl.Type.Int)), Bpl.Expr.Ident("#cev_pc", Bpl.Type.Int)), null, "CEV information"));
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
string name = wellformednessProc ? "CheckWellformed$$" + m.FullName : m.FullName;
Bpl.Procedure proc = new Bpl.Procedure(m.tok, name, typeParams, inParams, outParams, req, mod, ens);
return proc;
}
#region Refinement extension
void AddMethodRefinement(MethodRefinement m)
{
Contract.Requires(m != null);
Contract.Requires( sink != null && predef != null);
// r is abstract, m is concrete
Method r = m.Refined;
Contract.Assert( r != null);
Contract.Assert( m.EnclosingClass != null);
string name = "Refinement$$" + m.FullName;
string that = "that";
Bpl.IdentifierExpr heap = new Bpl.IdentifierExpr(m.tok, predef.HeapVarName, predef.HeapType);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, heap, that);
// TODO: this straight inlining does not handle recursive calls
// TODO: we assume frame allows anything to be changed -- we don't include post-conditions in the refinement procedure, or check refinement of frames
// generate procedure declaration with pre-condition wp(r, true)
Bpl.Procedure proc = AddMethod(r, false, true);
proc.Name = name;
// create "that" for m
Bpl.Expr wh = Bpl.Expr.And(
Bpl.Expr.Neq(new Bpl.IdentifierExpr(m.tok, that, predef.RefType), predef.Null),
etran.GoodRef(m.tok, new Bpl.IdentifierExpr(m.tok, that, predef.RefType), Resolver.GetThisType(m.tok, m.EnclosingClass)));
Bpl.Formal thatVar = new Bpl.Formal(m.tok, new Bpl.TypedIdent(m.tok, that, predef.RefType, wh), true);
proc.InParams.Add(thatVar);
// add outs of m to the outs of the refinement procedure
foreach (Formal p in m.Outs) {
Bpl.Type varType = TrType(p.Type);
Bpl.Expr w = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.UniqueName, varType), p.Type, etran);
proc.OutParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.UniqueName, varType, w), false));
}
sink.TopLevelDeclarations.Add(proc);
// generate procedure implementation:
Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
Bpl.VariableSeq inParams = Bpl.Formal.StripWhereClauses(proc.InParams);
Bpl.VariableSeq outParams = Bpl.Formal.StripWhereClauses(proc.OutParams);
Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
Contract.Assert( m.Body != null);
Contract.Assert( r.Body != null);
// declare a frame variable that allows anything to be changed (not checking modifies clauses)
CEVPrelude(m, inParams, outParams, builder);
Bpl.IdentifierExpr theFrame = etran.TheFrame(m.tok);
Contract.Assert( theFrame.Type != null);
Bpl.LocalVariable frame = new Bpl.LocalVariable(m.tok, new Bpl.TypedIdent(m.tok, theFrame.Name, theFrame.Type));
localVariables.Add(frame);
// $_Frame := (lambda $o: ref, $f: Field alpha :: true);
Bpl.TypeVariable alpha = new Bpl.TypeVariable(m.tok, "alpha");
Bpl.BoundVariable oVar = new Bpl.BoundVariable(m.tok, new Bpl.TypedIdent(m.tok, "$o", predef.RefType));
Bpl.BoundVariable fVar = new Bpl.BoundVariable(m.tok, new Bpl.TypedIdent(m.tok, "$f", predef.FieldName(m.tok, alpha)));
Bpl.Expr lambda = new Bpl.LambdaExpr(m.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null, Bpl.Expr.True);
builder.Add(Bpl.Cmd.SimpleAssign(m.tok, new Bpl.IdentifierExpr(m.tok, frame), lambda));
// assume I($Heap, $Heap)
builder.Add(new Bpl.AssumeCmd(m.tok, TrCouplingInvariant(m, heap, "this", heap, that)));
// assign input formals of m (except "this")
for (int i = 0; i < m.Ins.Count; i++) {
Bpl.LocalVariable arg = new Bpl.LocalVariable(m.tok, new Bpl.TypedIdent(m.tok, m.Ins[i].UniqueName, TrType(m.Ins[i].Type)));
localVariables.Add(arg);
Bpl.Variable var = inParams[i+1];
Contract.Assert( var != null);
builder.Add(Bpl.Cmd.SimpleAssign(m.tok, new Bpl.IdentifierExpr(m.tok, arg), new Bpl.IdentifierExpr(m.tok, var)));
}
// set-up method-translator state
currentMethod = m;
loopHeapVarCount = 0;
otherTmpVarCount = 0;
_phvie = null;
_nwie = null;
// call inlined m;
TrStmt(m.Body, builder, localVariables, etran);
// $Heap1 := $Heap;
Bpl.LocalVariable heap2 = new Bpl.LocalVariable(m.tok, new Bpl.TypedIdent(m.tok, heap.Name+"2", predef.HeapType));
localVariables.Add(heap2);
builder.Add(Bpl.Cmd.SimpleAssign(m.tok, new Bpl.IdentifierExpr(m.tok, heap2), etran.HeapExpr));
// $Heap := old($Heap);
builder.Add(Bpl.Cmd.SimpleAssign(m.tok, heap, new Bpl.OldExpr(m.tok, heap)));
// call inlined r;
currentMethod = r;
etran = new ExpressionTranslator(this, predef, heap, "this");
TrStmt(r.Body, builder, localVariables, etran);
// clean method-translator state
currentMethod = null;
loopHeapVarCount = 0;
otherTmpVarCount = 0;
_phvie = null;
_nwie = null;
// Contract.Assert( output variables of r and m are pairwise equal
Contract.Assert( outParams.Length % 2 == 0);
int k = outParams.Length / 2;
for (int i = 0; i < k; i++) {
Bpl.Variable rOut = outParams[i];
Bpl.Variable mOut = outParams[i+k];
Contract.Assert( rOut != null && mOut != null);
builder.Add(Assert(m.tok, Bpl.Expr.Eq(new Bpl.IdentifierExpr(m.tok, mOut), new Bpl.IdentifierExpr(m.tok, rOut)),
"Refinement method may not produce the same value for output variable " + m.Outs[i].Name));
}
// Contract.Assert( I($Heap1, $Heap)
builder.Add(Assert(m.tok, TrCouplingInvariant(m, heap, "this", new Bpl.IdentifierExpr(m.tok, heap2), that),
"Refinement method may not preserve the coupling invariant"));
Bpl.StmtList stmts = builder.Collect(m.tok);
Bpl.Implementation impl = new Bpl.Implementation(m.tok, proc.Name,
typeParams, inParams, outParams,
localVariables, stmts);
sink.TopLevelDeclarations.Add(impl);
}
private sealed class NominalSubstituter : Duplicator
{
private readonly Dictionary subst;
public NominalSubstituter(Dictionary subst) :base(){
Contract.Requires(cce.NonNullElements(subst));
this.subst = subst;
}
[ContractInvariantMethod]
void ObjectInvariant()
{
Contract.Invariant(cce.NonNullElements(subst));
}
public override Expr VisitIdentifierExpr(Bpl.IdentifierExpr node)
{
Contract.Requires(node != null);
Contract.Ensures(Contract.Result() != null);
if (subst.ContainsKey(node.Name))
return subst[node.Name];
else
return base.VisitIdentifierExpr(node);
}
}
Bpl.Expr TrCouplingInvariant(MethodRefinement m, Bpl.Expr absHeap, string absThis, Bpl.Expr conHeap, string conThis)
{
Contract.Requires(m != null);
Contract.Requires(absHeap != null);
Contract.Requires(absThis != null);
Contract.Requires(conHeap != null);
Contract.Requires(conThis != null);
Contract.Requires(predef != null);
Bpl.Expr cond = Bpl.Expr.True;
ClassRefinementDecl c = m.EnclosingClass as ClassRefinementDecl;
Contract.Assert( c != null);
ExpressionTranslator etran = new ExpressionTranslator(this, predef, conHeap, conThis);
foreach (MemberDecl d in c.Members)
if (d is CouplingInvariant) {
CouplingInvariant inv = (CouplingInvariant)d;
Contract.Assert( inv.Refined != null);
Contract.Assert( inv.Formals != null);
// replace formals with field dereferences
Dictionary map = new Dictionary();
Bpl.Expr absVar = new Bpl.IdentifierExpr(d.tok, absThis, predef.RefType);
for (int i = 0; i < inv.Refined.Count; i++) {
// TODO: boxing/unboxing?
Bpl.Expr result = Bpl.Expr.SelectTok(inv.Toks[i], absHeap, absVar, new Bpl.IdentifierExpr(inv.Toks[i], GetField(cce.NonNull(inv.Refined[i]))));
map.Add(inv.Formals[i].UniqueName, result);
}
Bpl.Expr e = new NominalSubstituter(map).VisitExpr(etran.TrExpr(inv.Expr));
cond = Bpl.Expr.And(cond, e);
}
return cond;
}
#endregion
class BoilerplateTriple { // a triple that is now a quintuple
[ContractInvariantMethod]
void ObjectInvariant()
{
Contract.Invariant(tok!=null);
Contract.Invariant(Expr != null);
Contract.Invariant(IsFree || ErrorMessage != null);
}
public readonly IToken tok;
public readonly bool IsFree;
public readonly Bpl.Expr Expr;
public readonly string ErrorMessage;
public readonly string Comment;
public BoilerplateTriple(IToken tok, bool isFree, Bpl.Expr expr, string errorMessage, string comment)
{
Contract.Requires(tok != null);
Contract.Requires(expr != null);
Contract.Requires( isFree || errorMessage != null);
this.tok = tok;
IsFree = isFree;
Expr = expr;
ErrorMessage = errorMessage;
Comment = comment;
}
}
///
/// There are 3 states of interest when generating two-state boilerplate:
/// S0. the beginning of the method, which is where the modifies clause is interpreted
/// S1. the pre-state of the two-state interval
/// S2. the post-state of the two-state interval
/// This method assumes that etranPre denotes S1, etran denotes S2, and that etran.Old denotes S0.
///
List/*!*/ GetTwoStateBoilerplate(IToken/*!*/ tok, Method/*!*/ method, ExpressionTranslator/*!*/ etranPre, ExpressionTranslator/*!*/ etran)
{
Contract.Requires(tok != null);
Contract.Requires(method != null);
Contract.Requires(etranPre != null);
Contract.Requires(etran != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List boilerplate = new List();
// the frame condition, which is free since it is checked with every heap update and call
boilerplate.Add(new BoilerplateTriple(tok, true, FrameCondition(tok, method.Mod, etranPre, etran), null, "frame condition"));
// HeapSucc(S1, S2)
Bpl.Expr heapSucc = FunctionCall(tok, BuiltinFunction.HeapSucc, null, etranPre.HeapExpr, etran.HeapExpr);
boilerplate.Add(new BoilerplateTriple(tok, true, heapSucc, null, "boilerplate"));
return boilerplate;
}
///
/// There are 3 states of interest when generating a freame condition:
/// S0. the beginning of the method, which is where the modifies clause is interpreted
/// S1. the pre-state of the two-state interval
/// S2. the post-state of the two-state interval
/// This method assumes that etranPre denotes S1, etran denotes S2, and that etran.Old denotes S0.
///
Bpl.Expr/*!*/ FrameCondition(IToken/*!*/ tok, List/*!*/ modifiesClause, ExpressionTranslator/*!*/ etranPre, ExpressionTranslator/*!*/ etran)
{
Contract.Requires(tok != null);
Contract.Requires(etran != null);
Contract.Requires(etranPre != null);
Contract.Requires(cce.NonNullElements(modifiesClause));
Contract.Requires( predef != null);
Contract.Ensures(Contract.Result() != null);
// generate:
// (forall o: ref, f: Field alpha :: { $Heap[o,f] }
// o != null && old($Heap)[o,alloc] ==>
// $Heap[o,f] == PreHeap[o,f] ||
// (o,f) in modifiesClause)
Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha");
Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType));
Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar);
Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha)));
Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar);
Bpl.Expr heapOF = Bpl.Expr.SelectTok(tok, etran.HeapExpr, o, f);
Bpl.Expr preHeapOF = Bpl.Expr.SelectTok(tok, etranPre.HeapExpr, o, f);
Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.Old.IsAlloced(tok, o));
Bpl.Expr consequent = Bpl.Expr.Eq(heapOF, preHeapOF);
consequent = Bpl.Expr.Or(consequent, InRWClause(tok, o, f, modifiesClause, etran.Old, null, null));
Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(heapOF));
return new Bpl.ForallExpr(tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), null, tr, Bpl.Expr.Imp(ante, consequent));
}
// ----- Type ---------------------------------------------------------------------------------
Bpl.Type TrType(Type type)
{
Contract.Requires(type != null);Contract.Requires( predef != null);
Contract.Ensures(Contract.Result() != null);
while (true) {
TypeProxy tp = type as TypeProxy;
if (tp == null) {
break;
} else if (tp.T == null) {
// unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type
return predef.RefType;
} else {
type = tp.T;
}
}
if (type is BoolType) {
return Bpl.Type.Bool;
} else if (type is IntType) {
return Bpl.Type.Int;
} else if (type.IsTypeParameter) {
return predef.BoxType;
} else if (type.IsRefType) {
// object and class types translate to ref
return predef.RefType;
} else if (type.IsDatatype) {
return predef.DatatypeType;
} else if (type is SetType) {
return predef.SetType(Token.NoToken, predef.BoxType);
} else if (type is SeqType) {
return predef.SeqType(Token.NoToken, predef.BoxType);
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type
}
}
Bpl.TypeVariableSeq TrTypeParamDecls(List/*!*/ tps)
{
Contract.Requires(cce.NonNullElements(tps));
Contract.Ensures(Contract.Result() != null);
Bpl.TypeVariableSeq typeParams = new Bpl.TypeVariableSeq();
return typeParams;
}
// ----- Statement ----------------------------------------------------------------------------
Bpl.AssertCmd Assert(Bpl.IToken tok, Bpl.Expr condition, string errorMessage)
{
Contract.Requires(tok != null);
Contract.Requires(condition != null);
Contract.Requires(errorMessage != null);
Contract.Ensures(Contract.Result() != null);
Bpl.AssertCmd cmd = new Bpl.AssertCmd(tok, condition);
cmd.ErrorData = "Error: " + errorMessage;
return cmd;
}
Bpl.AssertCmd AssertNS(Bpl.IToken tok, Bpl.Expr condition, string errorMessage)
{
Contract.Requires(tok != null);Contract.Requires(errorMessage != null);Contract.Requires(condition != null);Contract.Ensures(Contract.Result() != null);
List