//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
namespace Microsoft.Boogie {
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
public static class LambdaHelper {
public static Absy Desugar(Absy node, out List/*!*/ axioms, out List/*!*/ functions) {
Contract.Requires(node != null);
Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out functions)));
Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out axioms)));
Contract.Ensures(Contract.Result() != null);
LambdaVisitor v = new LambdaVisitor();
node = v.Visit(node);
axioms = v.lambdaAxioms;
functions = v.lambdaFunctions;
if (CommandLineOptions.Clo.TraceVerify) {
Console.WriteLine("Desugaring of lambda expressions produced {0} functions and {1} axioms:", functions.Count, axioms.Count);
TokenTextWriter wr = new TokenTextWriter("", Console.Out);
foreach (Function f in functions) {
f.Emit(wr, 0);
}
foreach (Expr ax in axioms) {
ax.Emit(wr);
Console.WriteLine();
}
}
return node;
}
public static void ExpandLambdas(Program prog) {
Contract.Requires(prog != null);
List/*!*/ axioms;
List/*!*/ functions;
Desugar(prog, out axioms, out functions);
foreach (var f in functions) {
prog.TopLevelDeclarations.Add(f);
}
foreach (var a in axioms) {
prog.TopLevelDeclarations.Add(new Axiom(a.tok, a));
}
}
private class LambdaVisitor : StandardVisitor {
internal List/*!*/ lambdaAxioms = new List();
internal List/*!*/ lambdaFunctions = new List();
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(cce.NonNullElements(lambdaAxioms));
Contract.Invariant(cce.NonNullElements(lambdaFunctions));
}
static int lambdaid = 0;
public override Program VisitProgram(Program prog) {
//Contract.Requires(prog != null);
Contract.Ensures(Contract.Result() != null);
foreach (Declaration/*!*/ decl in prog.TopLevelDeclarations) {
Contract.Assert(decl != null);
if (decl is Axiom || decl is Function) {
this.Visit(decl);
}
}
return prog;
}
public override Procedure VisitProcedure(Procedure node) {
//Contract.Requires(node != null);
Contract.Ensures(Contract.Result() != null);
// do not visit requires/ensures when calling this on Implementation
return node;
}
public override Absy Visit(Absy node) {
//Contract.Requires(node != null);
Contract.Ensures(Contract.Result() != null);
node = base.Visit(node);
LambdaExpr lambda = node as LambdaExpr;
if (lambda != null) {
IToken/*!*/ tok = lambda.tok;
Contract.Assert(tok != null);
Set freeVars = new Set();
lambda.ComputeFreeVariables(freeVars);
// this is ugly, the output will depend on hashing order
Hashtable subst = new Hashtable();
VariableSeq formals = new VariableSeq();
ExprSeq callArgs = new ExprSeq();
ExprSeq axCallArgs = new ExprSeq();
VariableSeq dummies = new VariableSeq(lambda.Dummies);
TypeVariableSeq freeTypeVars = new TypeVariableSeq();
List fnTypeVarActuals = new List();
TypeVariableSeq freshTypeVars = new TypeVariableSeq(); // these are only used in the lambda@n function's definition
foreach (object o in freeVars) {
// 'o' is either a Variable or a TypeVariable. Since the lambda desugaring happens only
// at the outermost level of a program (where there are no mutable variables) and, for
// procedure bodies, after the statements have been passified (when mutable variables have
// been replaced by immutable incarnations), we are interested only in BoundVar's and
// TypeVariable's.
BoundVariable v = o as BoundVariable;
if (v != null) {
TypedIdent ti = new TypedIdent(v.TypedIdent.tok, v.TypedIdent.Name, v.TypedIdent.Type);
Formal f = new Formal(v.tok, ti, true);
formals.Add(f);
BoundVariable b = new BoundVariable(v.tok, ti);
dummies.Add(b);
callArgs.Add(new IdentifierExpr(v.tok, v));
Expr/*!*/ id = new IdentifierExpr(f.tok, b);
Contract.Assert(id != null);
subst.Add(v, id);
axCallArgs.Add(id);
} else if (o is TypeVariable) {
TypeVariable tv = (TypeVariable)o;
freeTypeVars.Add(tv);
fnTypeVarActuals.Add(tv);
freshTypeVars.Add(new TypeVariable(tv.tok, tv.Name));
}
}
Formal res = new Formal(tok, new TypedIdent(tok, TypedIdent.NoName, cce.NonNull(lambda.Type)), false);
Function fn = new Function(tok, "lambda@" + lambdaid++, freshTypeVars, formals, res, "auto-generated lambda function", lambda.Attributes);
lambdaFunctions.Add(fn);
FunctionCall fcall = new FunctionCall(new IdentifierExpr(tok, fn.Name));
fcall.Func = fn; // resolve here
List selectArgs = new List();
foreach (Variable/*!*/ v in lambda.Dummies) {
Contract.Assert(v != null);
selectArgs.Add(new IdentifierExpr(v.tok, v));
}
NAryExpr axcall = new NAryExpr(tok, fcall, axCallArgs);
axcall.Type = res.TypedIdent.Type;
axcall.TypeParameters = SimpleTypeParamInstantiation.From(freeTypeVars, fnTypeVarActuals);
NAryExpr select = Expr.Select(axcall, selectArgs);
select.Type = lambda.Body.Type;
List selectTypeParamActuals = new List();
TypeVariableSeq forallTypeVariables = new TypeVariableSeq();
foreach (TypeVariable/*!*/ tp in lambda.TypeParameters) {
Contract.Assert(tp != null);
selectTypeParamActuals.Add(tp);
forallTypeVariables.Add(tp);
}
forallTypeVariables.AddRange(freeTypeVars);
select.TypeParameters = SimpleTypeParamInstantiation.From(lambda.TypeParameters, selectTypeParamActuals);
Expr bb = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), lambda.Body);
NAryExpr body = Expr.Eq(select, bb);
body.Type = Type.Bool;
body.TypeParameters = SimpleTypeParamInstantiation.EMPTY;
Trigger trig = new Trigger(select.tok, true, new ExprSeq(select));
lambdaAxioms.Add(new ForallExpr(tok, forallTypeVariables, dummies, lambda.Attributes, trig, body));
NAryExpr call = new NAryExpr(tok, fcall, callArgs);
call.Type = res.TypedIdent.Type;
call.TypeParameters = SimpleTypeParamInstantiation.From(freeTypeVars, fnTypeVarActuals);
return call;
}
return node;
}
}
}
} // end namespace