The Dafny compilation process translates Dafny programs into target language source code.

For a simple Dafny-only program, the translation step converts a A.dfy file into A.cs; the build step then produces a A.dll, which can be used as a library or as an executable.

A multi-language program that combines Dafny and C# code “just” needs to be sure that the translated Dafny code fits in to the C# code. There are two aspects to this:

Calling C# from Dafny

Calling a C# method from Dafny requires declaring a shim in Dafny that gives a name and types that can be referenced by Dafny code, while still having the same name as in the C# code.

For example, suppose we want to call a C# method Demo.p(). In Demo.cs we have

using System;
using System.IO;
public class Demo {
  public static void p() { Console.WriteLine("Hi!"); }
}

In Demo1.dfy we write,

module M {
  method {:extern "Demo", "p"} p() 
  method Main() {
    p();
  }
}

Note that the declaration of p() has no body; it is just a declaration because the actual implementation is in C#. Its extern attribute has two arguments: the fully-qualified class name and the method name.

Then the above can be built with dafny build --target:cs Demo1.dfy Demo.cs and then run as a dotnet program with dotnet Demo1.dll

Or, in one build-and-run step, dafny run Demo1.dfy --input Demo.cs

Calling non-static C# methods from Dafny

If the C# methods to be called are not static, then a receiver object must also be created and shared between C# and Dafny. The following example shows how to do this.

In a Demo1a.dfy file:

module {:extern "demo"} M {
  method {:extern "demo.Demo", "newDemo"} newDemo() returns (r: Demo )
  class {:extern "Demo" } Demo {
    method {:extern "demo.Demo", "p"} p()
  }
}
module MM {
  import opened M
  method Main() {
    var d: Demo := newDemo();
    d.p();
  }
}

In a Demo1x.cs file

using System;
using System.IO;

namespace demo {
  public class Demo {
    public static Demo newDemo() { return new Demo(); }
    public void p() { Console.WriteLine("Hi!"); }
  }
}

In the C# file there are two methods: a static one that returns a new instance of the Demo class, and a non-static method that does something useful. In the .dfy file we have a module M that has an extern name corresponding to the namespace of the C# class, a class declaration that associates the Dafny class Demo with the C# class demo.Demo, an instance method declaration that connects the Dafny method M.Demo.p to the C# method demo.Demo.p, and a static Dafny method M.newDemo connected to the static C# method demo.Demo.newDemo. The extern declaration for class Demo is actually not necessary if the Dafny and C# classes have the same name and the Dafny class is contained in a module whose extern name is the same as the C# namespace.

Then the Main method in the Dafny file calls the two Dafny methods, which are translated to the two C# methods. The combination is built and run using dafny run --target:cs Demo1a.dfy --input Demo1x.cs

Calling Dafny from C#

The simplest way to call a Dafny method from C# is to place the Dafny method in a class within a module. The module should be attributed as {:extern "M"}, where M is the desired name of the C# namespace in which the C# class resides. There is no need to give an extern name to the class or method.

In Demo2.dfy:

module {:extern "MM"} MM {
  class C {
    static method p() {
      print "Hello, World\n";
    }
  }
}

module MMM {
  class Demo2x {
    static method {:extern "Demo2x", "M"} M()
  }
}

method Main() { MMM.Demo2x.M(); }

In Demo2x.cs:

public class Demo2x {
  public static void M() {
    MM.C.p(); // Calls p() in Dafny
  }
}

The above program is run with dafny run --target:cs Demo2.dfy --input Demo2x.cs.

(Note that having Dafny invoke Main in the .cs code is not yet operational.)

Types

If the C# method has input arguments or an output value, then the Dafny declaration must use corresponding types in Dafny: Here, T' for a type parameter T indicates the C# type corresponding to a Dafny type T.

Dafny type C# type
bool bool
int System.Numerics.BigInteger
int64 long
int32 int
int16 short
int8 sbyte
char char
bitvector appropriate unsigned int-based type
ORDINAL java.math.BigInteger
real Dafny.BigRational
  double
  float
string Dafny.ISequence
JavaString java.lang.String
C, C? (for class, iterator C) (class) C
(trait) T (interface) T
datatype, codatatype (class) C
subset type same as base type
tuple _System._ITuple_n_
array T’[]
seq Dafny.ISequence<? extends T’>
set, iset Dafny.ISet<? extends T’>
multisetset Dafny.ISet<? extends T’>
map<T,U>, imap<T,U> Dafny.IMap<? extends T’>
imap<T,U>, imap<T,U> Dafny.IMap<? extends T’>
function (arrow) types Func<T’,U’>