docs/compilers/CSharp/Compiler Breaking Changes - VS2015.md
This document lists known breaking changes in Roslyn (VS2015 and later) from the native C# compiler (VS2013 and previous).
<!-- *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.* -->This page tracks known cases where the Roslyn compiler intentionally deviates from the previous versions or the language specifications. It includes both breaking changes and changes which enable things which would not be permitted by either the language specification or the native compiler. Bug numbers without links refer to internal Microsoft bug tracking systems.
<paramref> tag in a documentation comment applied to a read only property refers to the 'value' parameter (Bug 738679).from DevDiv #656739:
using System;
class InputParameter
{
public static implicit operator bool(InputParameter inputParameter)
{
throw null;
}
public static implicit operator int(InputParameter inputParameter)
{
throw null;
}
}
class Program
{
static void Main(string[] args)
{
InputParameter i1 = new InputParameter();
InputParameter i2 = new InputParameter();
bool b = i1 || i2;
}
}
The C# spec, section 7.12, says "An operation of the form x && y or x || y is processed by applying overload resolution (§7.3.4) as if the operation was written x & y or x | y."
Prior to DevDiv #656739, Roslyn reported that || was ambiguous in the example above because | would be. Unfortunately, dev11 only treats || as | when considering user-defined operators (or when one of the operands is dynamic). That is, for built-in operators, it distinguishes between || and |. Since there is no || operator for ints, there is no ambiguity.
Bug 9510: Error CS0182 (An attribute argument must be a constant expression) should not be issued for 'new' expressions of enum types
As per the specification, new expressions of enum types cannot have a constant value. However native compiler violates this for parameterless value type constructors and Roslyn maintains compatibility by constant folding parameterless value type constructors. This applies also to enum types.
// DELIBERATE SPEC VIOLATION:
// The spec does not allow "new int()" to be treated as a constant
// The native compiler treats "new int()" (and so on) as a "zero" constant expression,
// despite the fact that the specification does not include object creation expressions on the
// list of legal constant expressions.
See bug 4424
class C
{
public C(params int[] x) { }
}
class D : C
{
}
ACTUAL RESULT: error CS1729: 'C' does not contain a constructor that takes 0 arguments
EXPECTED RESULT: no errors (although the spec requires exactly parameterless ctor, Dev10 and previous versions always supported any ctors that can be invoked with an empty argument list)
According to the spec, this is illegal:
Object o = (null);
Because the null conversion doesn't apply to parenthesized null expressions, only null literals.
Maybe similar issues with lambdas.
Looking at the spec, the enclosed program would appear to be an error because of the following part of 7.4 (member lookup):
- Finally, having removed hidden members, the result of the lookup is determined:
- If the set consists of a single member that is not a method, then this member is the result of the lookup.
- Otherwise, if the set contains only methods, then this group of methods is the result of the lookup.
- Otherwise, the lookup is ambiguous, and a binding-time error occurs.
However, the Dev10 compiler accepts the code and gives only a warning that there is an ambiguity and it is using the method group rather than the property.
Roslyn uses the property and ignores the method (thus giving a type error in this particular case).
Do you think we should make all three (spec, Dev10, and Roslyn) behave the same?
delegate void MyAction<T>(T x);
interface I1
{
object Y { get; }
}
interface I2
{
void Y(long l);
}
interface I3 : I1, I2 { }
public class Program : I3
{
object I1.Y
{
get
{
return null;
}
}
void I2.Y(long l) { }
public static void Main(string[] args)
{
I3 p = new Program();
MyAction<long> o = p.Y; // lookup is ambiguous?
long l = 12;
o(l);
}
}
Dev10 doesn't report the following unreachable statement, and so we don't either (see bug 3552):
class Program
{
static void Main()
{
if (false)
{ // this block is unreachable
}
}
}
More precisely, Dev10 will not report when an "empty" statement, a throw statement, or a block statement itself is unreachable. However, Dev10 will complain if an unreachable block contains some other kind of (unreachable) statement.
Dev10 allows a static class to be used as the type of an interface method parameter and an interface method return type. This is disallowed by the language spec (10.1.1.3.1) but permitted by the Dev10 compiler.
We follow suit in Roslyn so as not to break compatibility with (probably useless) existing programs.
// NOTE: We extend the specification here. The C# 3.0 spec does not describe
// a "null type". Rather, it says that the null literal is typeless, and is
// convertible to any reference or nullable type. However, the C# 2.0 and 3.0
// implementations have a "null type" which some expressions other than the
// null literal may have. (For example, (null??null), which is also an
// extension to the specification.)
Technically, in the specification a parenthesized lambda expression is not an anonymous function expression (and is therefore never legal). However, Dev10 and Roslyn both ignore the parens (except for precedence), accepting code such as the following:
var tmp = (Func<int,int>)( x => x+1 );
The C# language spec says that hidden methods are removed early in the overload resolution process. However, that is false. The enclosed program (which works in Dev11 and Roslyn) demonstrates that methods with the same signature are not hidden.
using System;
class Base
{
public void M(int x) { Console.WriteLine("Base.M(x:" + x + ")"); }
}
class Derived : Base
{
public new void M(int y) { Console.WriteLine("Derived.M(y:" + y + ")"); }
}
public class Test
{
public static void Main()
{
Derived d = new Derived();
d.M(x: 1);
d.M(y: 2);
}
}
Clearly this behavior does not agree with the specification, and has been in the product long enough that it probably makes more sense to adjust the specification.
This is related to Roslyn bug 12989.
The native compiler (improperly) extended the C# language concept of reserved members to type parameters. For example, the following code is rejected by the native compiler:
public abstract class Foo<Item>
{
public Item this[int arg] { get { return default(Item); } }
}
Error: The type 'Foo<Item>' already contains a definition for 'Item'
Roslyn does not reject this code.
The source for the special type Int32 is a struct that contains a member of its own type. That is not allowed by the C# language specification, yet the compiler must allow it and properly compile that source to produce metadata for that primitive type.
Same for other primitive types.
The following test case should “pass” to match Dev10 behavior:
using System;
static class Program
{
static void Main()
{
const string x = "pass";
string y;
string z = x ?? y;
Console.WriteLine(z);
}
}
However, this is not justified by the current language specification. Since Dev10 accepts this code, I recommend we modify the specification to allow this.
Roslyn does not ignore method overloads that reference symbols from assemblies to which the compilation lacks an assembly reference. If one attempts to use a method group containing such a method, for example in overload resolution, such a method was ignored in prior compilers. In VS2015 an error CS0012 is given. See #9370
While the Roslyn C# compiler permits the elision of the index operator of an indexed property (declared, for example, in VB) when the property can be invoked with no parameters (e.g., it has a parameter with a default value), the C# compiler will not permit a (second) indexing of the result unless the first index operation was explicit. This is a breaking change from C# 5, which permitted the elision of the indexer that had default parameters. See #17045.