Skip to content

Commit

Permalink
[Dafny-to-Rust] Avoir using MaybePlacebo when assignments are straigh…
Browse files Browse the repository at this point in the history
…tforward (#5959)

### Description
Given the following dafny file
```dfy
method Test(i: int) returns (j: int) {
  j := i;
}

method Main() {
  var j := Test(1);
  var k := Test(j);
}
```
the Dafny to Rust code generator used to produce this:
```rs
    pub fn Test(i: &DafnyInt) -> DafnyInt {
      let mut j = MaybePlacebo::<DafnyInt>::new();
      j = MaybePlacebo::from(i.clone());
      return j.read();
    }
    pub fn Main(_noArgsParameter: &Sequence<Sequence<DafnyChar>>) -> () {
      let mut j = MaybePlacebo::<DafnyInt>::new();
      let mut _out0 = MaybePlacebo::<DafnyInt>::new();
      _out0 = MaybePlacebo::from(_default::Test(&int!(1)));
      j = MaybePlacebo::from(_out0.read());
      let mut k = MaybePlacebo::<DafnyInt>::new();
      let mut _out1 = MaybePlacebo::<DafnyInt>::new();
      _out1 = MaybePlacebo::from(_default::Test(&j.read()));
      k = MaybePlacebo::from(_out1.read());
      return ();
    }
```
While Dafny lets users declare a variable while assigning it
conditionally, Rust does not support this pattern as it needs to know if
the variable was assigned so that it can deallocate it. The current
solution supports all cases. It uses `MaybePlacebo`, a synonym of the
Option type, so that we can declare a placebo holder even if the type
does not implement the `Default trait` (looking at you, functions).
However, this encoding is still tracking a boolean, and furthermore, it
makes the code harder to read and debug.

With this PR, Dafny performs static analysis of assignments, determine
that the variables will be surely assigned, and is able to determine at
the declaration of the yet undefined variable if it's going to be surely
assigned or surely not assigned. In either case, it removes the
MaybePlacebo wrapper. This works for output variables of method calls,
as well as variables declared but not defined.

```rs
    pub fn Test(i: &DafnyInt) -> DafnyInt {
      let mut j: DafnyInt = i.clone();
      return j.clone();
    }
    pub fn Main(_noArgsParameter: &Sequence<Sequence<DafnyChar>>) -> () {
      let mut j: DafnyInt;
      let mut _out0: DafnyInt = _default::Test(&int!(1));
      j = _out0.clone();
      let mut k: DafnyInt;
      let mut _out1: DafnyInt = _default::Test(&j);
      k = _out1.clone();
      return ();
    }
```
There are still a few clones and one day we might be able to get rid of
the intermediate variable, but this is out of scope for this PR.

### How has this been tested?
- The test above has been added
- The behavior of determining assignment status has a thorough new test
executed from `DafnyCore.Test`

<small>By submitting this pull request, I confirm that my contribution
is made under the terms of the [MIT
license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>
  • Loading branch information
MikaelMayer authored Dec 4, 2024
1 parent de81b69 commit bf1aac6
Show file tree
Hide file tree
Showing 8 changed files with 1,028 additions and 516 deletions.
5 changes: 5 additions & 0 deletions Source/DafnyCore.Test/GeneratedDafnyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ public void TestRustASTCoverage() {
public void TestPathSimplification() {
FactorPathsOptimizationTest.__default.TestApply();
}

[Fact]
public void TestDefsCoverage() {
DefsCoverage.__default.Tests();
}
}
81 changes: 81 additions & 0 deletions Source/DafnyCore.Test/GeneratedFromDafny/DefsCoverage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Dafny program the_program compiled into C#
// To recompile, you will need the libraries
// System.Runtime.Numerics.dll System.Collections.Immutable.dll
// but the 'dotnet' tool in net6.0 should pick those up automatically.
// Optionally, you may want to include compiler switches like
// /debug /nowarn:162,164,168,183,219,436,1717,1718

using System;
using System.Numerics;
using System.Collections;
#pragma warning disable CS0164 // This label has not been referenced
#pragma warning disable CS0162 // Unreachable code detected
#pragma warning disable CS1717 // Assignment made to same variable

namespace DefsCoverage {

public partial class __default {
public static void Expect(bool x)
{
if (!(x)) {
throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-definitions.dfy(789,4): " + Dafny.Sequence<Dafny.Rune>.UnicodeFromString("expectation violation").ToVerbatimString(false));}
}
public static void Tests()
{
DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_SurelyAssigned()), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_NotAssigned()), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_SurelyAssigned()), Defs.AssignmentStatus.create_Unknown()));
DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_NotAssigned()), Defs.AssignmentStatus.create_Unknown()));
DefsCoverage.__default.Expect((object.Equals((Defs.AssignmentStatus.create_Unknown()).Join(Defs.AssignmentStatus.create_NotAssigned()), (Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_Unknown()))) && (object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown())));
DefsCoverage.__default.Expect((object.Equals((Defs.AssignmentStatus.create_Unknown()).Join(Defs.AssignmentStatus.create_SurelyAssigned()), (Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_Unknown()))) && (object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown())));
DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_Unknown()).Join(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown()));
DefsCoverage.__default.Expect((((object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_Unknown()), (Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_NotAssigned()))) && (object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_NotAssigned()), (Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned())))) && (object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned()), (Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned())))) && (object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned()), Defs.AssignmentStatus.create_SurelyAssigned())));
DefsCoverage.__default.Expect((((object.Equals((Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_NotAssigned()), (Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_SurelyAssigned()))) && (object.Equals((Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_SurelyAssigned()), (Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_Unknown())))) && (object.Equals((Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_Unknown()), (Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_Unknown())))) && (object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown())));
DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_NotAssigned()), Defs.AssignmentStatus.create_NotAssigned()));
Dafny.ISequence<Dafny.Rune> _0_x;
_0_x = Dafny.Sequence<Dafny.Rune>.UnicodeFromString("x");
Dafny.ISequence<Dafny.Rune> _1_y;
_1_y = Dafny.Sequence<Dafny.Rune>.UnicodeFromString("y");
DAST._IExpression _2_z;
_2_z = DAST.Expression.create_Ident(Dafny.Sequence<Dafny.Rune>.UnicodeFromString("z"));
Dafny.ISequence<DAST._IStatement> _3_assigns__x;
_3_assigns__x = Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Assign(DAST.AssignLhs.create_Ident(_0_x), DAST.Expression.create_Ident(_1_y)));
Dafny.ISequence<DAST._IStatement> _4_assigns__y;
_4_assigns__y = Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Assign(DAST.AssignLhs.create_Ident(_1_y), DAST.Expression.create_Ident(_0_x)));
DAST._IExpression _5_cond;
_5_cond = DAST.Expression.create_Ident(Dafny.Sequence<Dafny.Rune>.UnicodeFromString("cond"));
Dafny.ISequence<DAST._IStatement> _6_unknown__x;
_6_unknown__x = Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_If(_5_cond, _3_assigns__x, _4_assigns__y));
Dafny.ISequence<DAST._IStatement> _7_surely__double__x;
_7_surely__double__x = Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_If(_5_cond, _3_assigns__x, _3_assigns__x));
Dafny.ISequence<DAST._IStatement> _8_call__to__x;
_8_call__to__x = Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Call(_2_z, DAST.CallName.create_SetBuilderAdd(), Dafny.Sequence<DAST._IType>.FromElements(), Dafny.Sequence<DAST._IExpression>.FromElements(), Std.Wrappers.Option<Dafny.ISequence<Dafny.ISequence<Dafny.Rune>>>.create_Some(Dafny.Sequence<Dafny.ISequence<Dafny.Rune>>.FromElements(_0_x))));
Dafny.ISequence<DAST._IStatement> _9_declare__x__again;
_9_declare__x__again = Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_DeclareVar(_0_x, DAST.Type.create_Tuple(Dafny.Sequence<DAST._IType>.FromElements()), Std.Wrappers.Option<DAST._IExpression>.create_None()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_4_assigns__y, _0_x), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_3_assigns__x, _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_3_assigns__x, _1_y), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(_3_assigns__x, _4_assigns__y), _1_y), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_6_unknown__x, _0_x), Defs.AssignmentStatus.create_Unknown()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_7_surely__double__x, _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_7_surely__double__x, _1_y), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_8_call__to__x, _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_8_call__to__x, _1_y), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(_8_call__to__x, _4_assigns__y), _1_y), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Labeled(Dafny.Sequence<Dafny.Rune>.UnicodeFromString("l"), _4_assigns__y)), _3_assigns__x), _1_y), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Labeled(Dafny.Sequence<Dafny.Rune>.UnicodeFromString("l"), _3_assigns__x)), _4_assigns__y), _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Labeled(Dafny.Sequence<Dafny.Rune>.UnicodeFromString("l"), _3_assigns__x)), _4_assigns__y), _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(_9_declare__x__again, _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(_9_declare__x__again, _4_assigns__y), _1_y), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Return(_2_z)), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_EarlyReturn()), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_JumpTailCallStart()), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Print(_2_z)), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_Print(_2_z)), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_SurelyAssigned()));
DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence<DAST._IStatement>.Concat(Dafny.Sequence<DAST._IStatement>.FromElements(DAST.Statement.create_While(_2_z, Dafny.Sequence<DAST._IStatement>.FromElements())), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_Unknown()));
}
public static Dafny.ISequence<Dafny.Rune> IND { get {
return RAST.__default.IND;
} }
}
} // end of namespace DefsCoverage
Loading

0 comments on commit bf1aac6

Please sign in to comment.