Skip to content
Branch: master
Find file Copy path
Find file Copy path
4 contributors

Users who have contributed to this file

@jcouv @gafter @333fred @chsienki
107 lines (90 sloc) 5.29 KB

*This document lists known breaking changes in Roslyn 3.0 (Visual Studio 2019) from Roslyn 2. (Visual Studio 2017)

Breaks are formatted with a monotonically increasing numbered list to allow them to referenced via shorthand (i.e., "known break #1"). Each entry should include a short description of the break, followed by either a link to the issue describing the full details of the break or the full details of the break inline.

  1. C# will now preserve left-to-right evaluation for compound assignment addition/subtraction expressions where the left-hand side is dynamic. In this example code:
    class DynamicTest
        public int Property { get; set; }
        static dynamic GetDynamic() => return new DynamicTest();
        static int GetInt() => 1;
        public static void Main() => GetDynamic().Property += GetInt();
  • Previous versions of Roslyn would have evaluated this as:
    1. GetInt()
    2. GetDynamic()
    3. get_Property
    4. set_Property
  • We now evaluate it as
    1. GetDynamic()
    2. get_Property
    3. GetInt()
    4. set_Property
  1. Previously, we allowed adding a module with Microsoft.CodeAnalysis.EmbeddedAttribute or System.Runtime.CompilerServices.NonNullTypesAttribute types declared in it. In Visual Studio 2019, this produces a collision error with the injected declarations of those types.

  2. Previously, you could refer to a System.Runtime.CompilerServices.NonNullTypesAttribute type declared in a referenced assembly. In Visual Studio 2019, the type from assembly is ignored in favor of the injected declaration of that type.

  3. Previously, ref-returning async local functions would compile, by ignoring the ref modifier of the return type. In Visual Studio 2019, this now produces an error, just like ref-returning async methods do.

  4. C# will now produce a warning for an expression of the form e is _. The is_type_expression can be used with a type named _. However, in C# 8 we are introducing a discard pattern written _ (which cannot be used as the pattern of an is_pattern_expression). To reduce confusion, there is a new warning when you write e is _:

    (11,31): warning CS8413: The name '_' refers to the type 'Program1._', not the discard pattern. Use '@_' for the type, or 'var _' to discard.
        bool M1(object o) => o is _;
  5. C# 8 produces a warning when a switch statement uses a constant named _ as a case label.

    (1,18): warning CS8512: The name '_' refers to the constant '_', not the discard pattern. Use 'var _' to discard the value, or '@_' to refer to a constant by that name.
    switch(e) { case _: break; }
  6. In C# 8.0, the parentheses of a switch statement are optional when the expression being switched on is a tuple expression, because the tuple expression has its own parentheses:

    switch (a, b)

Due to this the OpenParenToken and CloseParenToken fields of a SwitchStatementSyntax node may now sometimes be empty.

  1. In an is-pattern-expression, a warning is now issued when a constant expression does not match the provided pattern because of its value. Such code was previously accepted but gave no warning. For example
    if (3 is 4) // warning: the given expression never matches the provided pattern.

We also issue a warning when a constant expression always matches a constant pattern in an is-pattern-expression. For example

    if (3 is 3) // warning: the given expression always matches the provided constant.

Other cases of the pattern always matching (e.g. e is var t) do not trigger a warning, even when they are known by the compiler to produce an invariant result.

  1. In C# 8, we give a warning when an is-type expression is always false because the input type is an open class type and the type it is tested against is a value type:
  class C<T> { }
  void M<T>(C<T> x)
      if (x is int) { } // warning: the given expression is never of the provided ('int') type.

previously, we gave the warning only in the reverse case

  void M<T>(int x)
      if (x is C<T>) { } // warning: the given expression is never of the provided ('C<T>') type.
  1. Previously, reference assemblies were emitted including embedded resources. In Visual Studio 2019, embedded resources are no longer emitted into ref assemblies. See

  2. Ref structs now support disposal via pattern. A ref struct enumerator with an accessible void Dispose() instance method will now have it invoked at the end of enumeration, regardless of whether the struct type implements IDisposable:

public class C
    public ref struct RefEnumerator
        public int Current => 0;
        public bool MoveNext() => false;
        public void Dispose() => Console.WriteLine("Called in C# 8.0 only");

    public RefEnumerator GetEnumerator() => new RefEnumerator();

    public static void Main()
        foreach(var x in new C())
        // RefEnumerator.Dispose() will be called here in C# 8.0
You can’t perform that action at this time.