QDK 0.3 language release notes and migration guide

We are excited to introduce the newest update to the Quantum Development Kit. This page gives an overview of the new Q# language features and describes how to migrate existing Q# programs to the new syntax. See our complete documentation for the full description of the Q# language here.

What's New

The 0.3 update comes with a range of new language and editor features.

Overview of Features

  • Enhanced development tool integration for Visual Studio and Visual Studio Code, including:
    • Live compilation and feedback on typing (a.k.a. squiggly underlining).
    • Hover information including documentation summaries and type signatures.
    • Support for go to definition.
    • Easy navigation to namespace, operation, function, and type declarations.
    • Improved diagnostic messages.
  • Language improvements
    • Single qubit and tuple initializations within using and borrowing
    • Tuple deconstruction on all assignments
    • Iteration over arrays
    • Conditional expressions
    • Default-specialization abbreviation for operations
    • Auto-generated type constructors
    • Expression modifiers

Editor Features

The Quantum Development Kit now includes a language server for Q#, as well as the client integrations for Visual Studio and Visual Studio Code. This enables a new set of IntelliSense features along with live feedback on typing in form of squiggly underlinings of errors and warnings. In particular, go to definition is now supported and hover information is displayed containing type information as well as information from documenting comments. Additionally, symbol information about defined namespaces, operations, functions, and types is available and allows to quickly navigate to declarations.
This update greatly improves diagnostic messages in general, with easy navigation to and precise ranges for diagnostics and additional details in the displayed hover information.

For this 0.3 release, the language server included with the Quantum Development Kit does not support multiple workspaces. In order to work with a project in VS Code, open the root folder containing the project itself and all referenced projects. In order to work with a solution in Visual Studio, all projects contained in the solution need to be in the same folder as the solution or in one of its subfolders.

Language Features

In terms of language features, this update unifies the treatment of a range of language patterns. Type constructors, as an example, are generated for each user defined type and can be partially applied much like any other function. Another example is tuple deconstruction, which is now fully supported within all assignments. This includes not only let-, mutable-, and set-statements, but also the iteration variable in for-loops as well as using- and borrowing-allocations. Additionally, partial deconstructions are newly supported with the 0.3 update; underscores in deconstructions indicate parts of the value that are to be ignored.

The following code illustrates some of the new features:

    let tuples = [(1, 0), (0, 1)];
    mutable res = (0, 0, 0);

    // For-loops can iterate over arrays, and can destructure tuples.
    for ((i1, i2) in tuples) {

        // Mutable assignments can now destructure and ignore parts of tuples,
        // using the same syntax as let-bindings.
        mutable (r1, r2, _) = res;
        set (r1, r2) = (r1 ||| i1, r2 &&& i2);

        let (_, _, s) = res;
        // The new conditional operator can be used inside expressions, avoiding
        // the need for extraneous if-statements.
        set res = (r1, r2, r1 == r2 ? s | s + 1);

The last set statement uses a new ternary operator that is introduced with the 0.3 update to support conditional expressions. A conditional expression is an expression of the form condition ? caseTrue | caseFalse. As can be seen from the example above, iteration over arrays is now also supported.

Allocations within using and borrowing are newly possible for single qubits, qubit arrays, and nested tuples thereof:

    using (qubit = Qubit()) {
        // qubit contains a single qubit

    borrowing ((qubits, qubit) = (Qubit[3], Qubit())) {
        // qubits contains an array of three qubits, and qubit contains a single qubit

Two new specialization generator directives invert and distribute in addition to auto are introduced to increase control over how functor specializations are generated. An explicit declaration of the default body specialization within operations is no longer mandatory. Similar to functions, statements may be directly added to the operation declaration itself if no other specialization is declared within the operation. A migration script is included in the release to help migrating existing code and taking advantage of new language features (see the section on the migration script).

A major change in how user-defined types are treated comes with the 0.3 update. While prior releases treated user defined types as subtypes of their underlying type, this is no longer the case going forward (see also the section on breaking changes).
As part of this change we introduce expression modifiers that can be applied to certain expressions. The "atomic" expressions that modifiers can be applied to are identifiers, array item expressions, and arity-1 tuples. Modifiers bind tighter than any other expression combinator, and in particular tighter than the call-expression combinator ( ). Adjoint, Controlled, and a new "unwrap" postfix operator ! are treated as expression modifiers. Treating Adjoint and Controlled as expression modifiers eliminates the need for parenthesis for a lot of cases. The expressions below for example are valid going forward for Op, a unitary operation, opArr an array of unitary operations, and arg a suitable argument:

    Adjoint Op (arg);
    Controlled opArr[i] (arg);

The migration script will drop most of the unnecessary parenthesis and take full advantage of the new features. However, be advised that the script does not interpret the code, and will not account for the different interpretation of the Controlled functor in rare cases (only relevant in combination with partial applications).

The new modifier ! casts a user-defined type to its underlying type. This cast has been considered an upcast in previous releases and was executed automatically. With the new interpretation of user defined types this is no longer the case and the cast needs to be made explicit.
The following example illustrates how to use the new modifier:

    newtype Unitary = (Qubit => Unit: Adjoint, Controlled);

    operation Foo (unitaries : Unitary[], qubit : Qubit) : Unit {

        for (unitary in unitaries[1 .. Length(unitaries)-1]) {
            // Each element of unitaries is an instance of the user-defined
            // type Unitary, so unitary! unwraps each element to an operation
            // type that we can call.
            unitary! (qubit);
            // The unwrap operator can also be used as a part of functor
            // expressions and when indexing into arrays.
            Adjoint unitary! (qubit);
            Adjoint unitaries[0]! (qubit);

As can be seen from the example, ! binds tighter than prefix modifiers, like Controlled and Adjoint.

Breaking Changes

The 0.3 update comes with a new compiler and contains a handful of breaking changes. To facilitate updating existing code, the compiler also provides a formatting option that compiles valid or invalid code and emits formatted Q# code based on the built compilation. All syntax changes will be processed automatically by the provided tool. However, changes in how valid code is interpreted (semantic changes) may need to be made manually.

In addition to the changes listed below, the pattern __*__ where * is any sequence of non-whitespace characters is reserved for internal use and cannot be used as a symbol name.

Syntax Changes

  • Starting with the version 0.3, array items are no longer separated by semicolons, but are separated by commas instead.
  • A semicolon on the other hand is required after an auto-generation directive for specialization declarations.
  • Conditions in if-, elif-, and until-clauses need to be encapsulated in parenthesis.

For all other syntax changes the compiler will simply generate a warning if the old syntax is used.
One example for such a change is the symbol tuple in user-defined specializations. While in previous releases a controlled or controlled-adjoint specialization declaration specified a single symbol argument, starting in 0.3 it is encouraged to provide such a symbol tuple for all specialization with ... indicating that the compiler should migrate the symbols from the callable declaration if needed.

Semantic Changes

The most impactful breaking for the 0.3 release is how user-defined types are integrated into the type system. In prior releases user defined types were considered to be a subtype of their underlying type. With this release and going forward this is no longer the case. User defined types are now considered to be their own distinct type, and no automatic cast between a user defined type and its underlying type exists. All casts need to be made explicit via an "unwrap" operator !, and a cast is needed in particular to access the content of a object of user defined type.

The modifications to the type system also include the variance behavior of arrays, and the treatment of type parameterized objects. Starting in 0.3, both mutable and immutable arrays are invariant. Additionally, arguments passed to a callable cannot be type parameterized. In these cases, type arguments must be passed along with the identifer.

Previous releases of the Q# compiler allowed a function value to be provided where an operation value was expected, as long as the function value had the correct input and output types. This was unintended behavior that is not specified in the Q# language specification. In the 0.3 compiler, we have removed this behavior so that function values are no longer considered operation values. To minimize the disruption to existing code that relied on this bug, we have added the ToOperation function in the Microsoft.Quantum.Canon namespace that will explicitly convert a function into an operation with the same input and output types.

The following example illustrates how to use the ToOperation function to change a function Square to an operation op:

namespace Microsoft.Quantum.Tests {
    open Microsoft.Quantum.Primitive;
    open Microsoft.Quantum.Canon;

    function Square(x : Int) : Int {
        return x * x;

    operation ApplyOp<'T, 'U>(op : ('T => 'U), input : 'T) : 'U {
        return op(input);

    operation ToOperationTest() : Unit {
        let op = ToOperation(Square);
        AssertIntEqual(ApplyOp(op, 3), 9, "ToOperation failed with Square.");


The release is supported on Visual Studio 2017 version 15.8 or later for Windows 10, and on Visual Studio Code version 1.27.2 or later for Windows 10, macOS, and Linux.

The Quantum Development Kit uses the .NET Core SDK (2.0 or later).


Follow the installation instructions here.

You may see warnings ("Found conflicts between different versions of "System.Reflection.Metadata" that could not be resolved.") if your current version of .NET Core SDK is not up to date. However, if you are using .NET Core SDK (2.0 or later), these warnings may be ignored.

  • The release is provided along with a number of different samples showing how to use both existing features of the Quantum Development Kit, and the new features available with this release. These samples can be can be found on GitHub at the Microsoft/Quantum repository.

Migrating Existing Projects to Q# 0.3

If you have existing Q# projects from version 0.2 of the Quantum Development Kit, the following are the steps to migrate those projects to the newest version. We also provide a migration script to help you with the process.


Projects need to be upgraded in order. If you have a solution with multiple projects update each project in the order they are referenced.

  1. From a command line, Run dotnet clean to remove all existing binaries and intermediate files.
  2. In a text editor, edit the .csproj file to change the version of all the "Microsoft.Quantum" PackageReference to version 0.3.1811.2802-preview, for example:
    <PackageReference Include="Microsoft.Quantum.Canon" Version="0.3.1811.2802" />
    <PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.3.1811.2802" />
  1. From the command line, use the formatting tool integrated into the command line compiler to address all syntax changes by running this command: dotnet msbuild /t:qsharpformat
    • Your files will be migrated in-place. A backup of all the original files will be copied to obj\qsharp\.backup
    • The formatting tool will compile the project ignoring all compilation errors and generate formatted Q# code based on the build compilation. Any unrecognized symbol (e.g. an undefined variable name) will be replaced by a placeholder text that needs to be replaced manually after formatting. In this case, the formatting succeeds while generating a warning for the affected file.
    • The formatting and in particular any white space in the file will be changed in the emitted code. Comments will be preserved.
  2. After running this, you might still need to manually address semantic changes in cases where the semantic interpretation of the code has changed. All these errors will be reported by IntelliSense in Visual Studio or Visual Studio Code.
    • Open the root folder of the project or the containing solution in Visual Studio 2017 or Visual Studio Code.
    • After opening a .qs file in the editor, you should see the output of the Q# language extension in the output window.
    • After the project has loaded successfully (indicated in the output window) open each file and manually to address all remaining issues.


  • For the 0.3 release, the language server included with the Quantum Development Kit does not support multiple workspaces.
  • In order to work with a project in Visual Studio Code, open the root folder containing the project itself and all referenced projects.
  • In order to work with a solution in Visual Studio, all projects contained in the solution need to be in the same folder as the solution or in one of its subfolders.
  • References between projects migrated to 0.3 and higher and projects using older package versions are not supported.

Migration script

In order to facilitate project migration, a PowerShell script is provided that can be downloaded here. This script helps migrate projects from version 0.2 of the Quantum Development Kit to use version 0.3.


The migration script requires Windows PowerShell or PowerShell Core to run. Windows PowerShell comes pre-installed with Windows 10. Download PowerShell Core for Windows, macOS, or Linux at https://github.com/PowerShell/PowerShell.

The script executes the following four steps:

  • The project is built with the previous version of the Quantum Development Kit to ensure that migration is likely to succeed. This step can be skipped by using the -Force parameter to the script.
  • New project templates are installed.
  • The project is updated to use the current version of the Quantum Development Kit.
  • The formatting tool provided with version 0.3 of the Quantum Development Kit is applied to automatically address most of the breaking changes documented above.

After these four steps complete, there may be some remaining migration issues which must be handled manually. Using the latest version of the Quantum Development Kit extensions for Visual Studio 2017 and Visual Studio Code can help find these issues easily.

The documentation for the script can be obtained by running one of the following commands in the folder where the script is located:

Get-Help ./qdk-migrate.ps1
Get-Help -Online ./qdk-migrate.ps1


By default, Windows prevents the execution of any scripts as a security measure. To allow scripts such as qdk-migrate.ps1 to run on Windows, you may need to change the execution policy. To do so, run the Set-ExecutionPolicy command:

Set-ExecutionPolicy RemoteSigned -Scope Process

The execution policy will then be reverted when you exit PowerShell. If you would like to save the execution policy, use a different value for -Scope:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser