Skip to content

Commit

Permalink
Fix: Tail-Recursion for the Dafny-to-Rust compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
MikaelMayer committed Aug 6, 2024
1 parent b9757ff commit ce30d26
Show file tree
Hide file tree
Showing 8 changed files with 1,740 additions and 1,700 deletions.
6 changes: 6 additions & 0 deletions Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -690,13 +690,19 @@ protected override ConcreteSyntaxTree EmitMethodReturns(Method m, ConcreteSyntax

protected override ConcreteSyntaxTree EmitTailCallStructure(MemberDecl member, ConcreteSyntaxTree wr) {
if (wr is BuilderSyntaxTree<StatementContainer> stmtContainer) {
// TODO: Emit new variables to store the input parameters,

var recBuilder = stmtContainer.Builder.TailRecursive();
return new BuilderSyntaxTree<StatementContainer>(recBuilder, this);
} else {
throw new InvalidOperationException();
}
}

public override string TailRecursiveVar(int inParamIndex, IVariable variable) {
return preventShadowing ? base.TailRecursiveVar(inParamIndex, variable) : DCOMP.COMP.TailRecursionPrefix.ToVerbatimString(false) + inParamIndex;
}

protected override void EmitJumpToTailCallStart(ConcreteSyntaxTree wr) {
if (wr is BuilderSyntaxTree<StatementContainer> stmtContainer) {
stmtContainer.Builder.AddStatement((DAST.Statement)DAST.Statement.create_JumpTailCallStart());
Expand Down
12 changes: 10 additions & 2 deletions Source/DafnyCore/Backends/Rust/Dafny-compiler-rust.dfy
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,8 @@ module {:extern "DCOMP"} DafnyToRustCompiler {

const ObjectType: ObjectType

static const TailRecursionPrefix := "_r"

var error: Option<string>

constructor(unicodeChars: bool, objectType: ObjectType) {
Expand Down Expand Up @@ -3257,22 +3259,28 @@ module {:extern "DCOMP"} DafnyToRustCompiler {
generated := generated.Then(R.DeclareVar(R.MUT, "_this", None, Some(selfClone)));
}
newEnv := env;
var loopBegin := R.RawExpr("");
for paramI := 0 to |env.names| {
var param := env.names[paramI];
var paramInit, _, _ := GenIdent(param, selfIdent, env, OwnershipOwned);
generated := generated.Then(R.DeclareVar(R.MUT, param, None, Some(paramInit)));
var recVar := TailRecursionPrefix + Strings.OfNat(paramI);
generated := generated.Then(R.DeclareVar(R.MUT, recVar, None, Some(paramInit)));
if param in env.types {
// We made the input type owned by the variable.
// so we can remove borrow annotations.
var declaredType := env.types[param].ToOwned();
newEnv := newEnv.AddAssigned(param, declaredType);
newEnv := newEnv.AddAssigned(recVar, declaredType);
}
// Redeclare the input parameter, take ownership of the recursive value
loopBegin := loopBegin.Then(R.DeclareVar(R.CONST, param, None, Some(R.Identifier(recVar))));
}
var bodyExpr, bodyIdents, bodyEnv := GenStmts(body, if selfIdent != NoSelf then ThisTyped("_this", selfIdent.dafnyType) else NoSelf, newEnv, false, earlyReturn);
readIdents := bodyIdents;
generated := generated.Then(
R.Labelled("TAIL_CALL_START",
R.Loop(None, bodyExpr)));
R.Loop(None,
loopBegin.Then(bodyExpr))));
}
case JumpTailCallStart() => {
generated := R.Continue(Some("TAIL_CALL_START"));
Expand Down
1 change: 1 addition & 0 deletions Source/DafnyCore/Backends/Rust/RustCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using Dafny;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4543,6 +4543,10 @@ void TrTailCallStmt(IToken tok, Method method, Expression receiver, List<Express
TrTailCall(tok, method.IsStatic, method.Ins, receiver, args, wr);
}

public virtual string TailRecursiveVar(int inParamIndex, IVariable variable) {
return IdName(variable);
}

void TrTailCall(IToken tok, bool isStatic, List<Formal> inParameters, Expression receiver, List<Expression> args, ConcreteSyntaxTree wr) {
// assign the actual in-parameters to temporary variables
var inTmps = new List<string>();
Expand Down Expand Up @@ -4582,13 +4586,18 @@ void TrTailCall(IToken tok, bool isStatic, List<Formal> inParameters, Expression
EndStmt(wr);
n++;
}

var inParamIndex = 0;
foreach (var p in inParameters) {
if (!p.IsGhost) {
EmitIdentifier(
inTmps[n],
EmitAssignment(IdentLvalue(IdName(p)), p.Type, inTypes[n], wr, tok)
);
// We want to assign the value to input parameters. However, if input parameters were shadowed
// for the compilers that support the same shadowing rules as Dafny (e.g. the Dafny-to-Rust compiler)
// we need to assign the result to the temporary and mutable variables instead
var wrAssignRhs =
EmitAssignment(IdentLvalue(TailRecursiveVar(inParamIndex, p)), p.Type, inTypes[n], wr, tok);
EmitIdentifier(inTmps[n], wrAssignRhs);
n++;
inParamIndex++;
}
}
Contract.Assert(n == inTmps.Count);
Expand Down
Loading

0 comments on commit ce30d26

Please sign in to comment.