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'> | |-------------------------------|------------------------------|