Skip to content

Commit

Permalink
Fixing a compat issue with invoking properties on generic type parame…
Browse files Browse the repository at this point in the history
…ters in expression trees.

While the casts are semantically incorrect, the conditions under which they are observable are extremely narrow:
We would have to deal with a generic T receiver which is actually a struct that implements a property form an interface and the implementation of the getter must make observable mutations to the instance.

At this point it seems more appropriate to continue adding these casts.

Fixes dotnet#4471
  • Loading branch information
VSadov committed Sep 8, 2015
1 parent 1bd0017 commit af83124
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,23 @@ private BoundExpression VisitPropertyAccess(BoundPropertyAccess node)
{
var receiver = node.PropertySymbol.IsStatic ? _bound.Null(ExpressionType) : Visit(node.ReceiverOpt);
var getMethod = node.PropertySymbol.GetOwnOrInheritedGetMethod();

// COMPAT: see https://github.com/dotnet/roslyn/issues/4471
// old compiler used to insert casts like this and
// there are known dependencies on this kind of tree shape.
//
// While the casts are semantically incorrect, the conditions
// under which they are observable are extremely narrow:
// We would have to deal with a generic T receiver which is actually a struct
// that implements a property form an interface and
// the implementation of the getter must make observable mutations to the instance.
//
// At this point it seems more appropriate to continue adding these casts.
if (node.ReceiverOpt?.Type.IsTypeParameter() == true)
{
receiver = this.Convert(receiver, getMethod.ReceiverType, isChecked: false);
}

return ExprFactory("Property", receiver, _bound.MethodInfo(getMethod));
}

Expand Down
94 changes: 89 additions & 5 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4159,6 +4159,48 @@ public static void Test1<T>() where T : I
var r1 = f(default(T));
Expression<Func<T, int>> e = x => x.M() + x.P + x.P;
var r2 = e.Compile()(default(T));
Console.WriteLine(r1!=r2 ? ""pass"" : ""fail"");
}
static void Main()
{
Test1<S>();
}
}";

string expectedOutput = @"pass";

CompileAndVerify(
new[] { source },
new[] { ExpressionAssemblyRef },
expectedOutput: expectedOutput);
}

[WorkItem(530529, "DevDiv")]
[Fact]
public void BoxTypeParameter1()
{
string source =
@"using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
interface I
{
int P();
}
struct S : I
{
int _p;
public int P() => _p++;
}
class Test
{
public static void Test1<T>() where T : I
{
Func<T, int> f = x => x.P() + x.P();
var r1 = f(default(T));
Expression<Func<T, int>> e = x => x.P() + x.P();
var r2 = e.Compile()(default(T));
Console.WriteLine(r1==r2 ? ""pass"" : ""fail"");
}
static void Main()
Expand All @@ -4175,6 +4217,27 @@ static void Main()
expectedOutput: expectedOutput);
}

[Fact, WorkItem(531047, "DevDiv")]
public void NullIsRegression()
{
string source =
@"using System;
using System.Linq.Expressions;
class Test
{
public static void Main()
{
Expression<Func<bool>> expr = () => null is Test;
Console.WriteLine(expr.Dump());
}
}";
string expectedOutput = "TypeIs(Constant(null Type:System.Object) TypeOperand:Test Type:System.Boolean)";
CompileAndVerify(
new[] { source, ExpressionTestLibrary },
new[] { ExpressionAssemblyRef },
expectedOutput: expectedOutput);
}

[WorkItem(546601, "DevDiv")]
[Fact]
public void NewArrayInitInAsAndIs()
Expand Down Expand Up @@ -4209,22 +4272,43 @@ public static void Main()
expectedOutput: expectedOutput);
}

[WorkItem(531047, "DevDiv")]
[Fact, WorkItem(531047, "DevDiv")]
public void NullIsRegression()
[Fact, WorkItem(4471, "https://github.com/dotnet/roslyn/issues/4471")]
public void GenericPropertyReceiverCast()
{
string source =
@"using System;
using System.Linq.Expressions;
class Test
{
public interface IDeletedID
{
int DeletedID { get; }
}
public class C1 : IDeletedID
{
int IDeletedID.DeletedID
{
get
{
return 1;
}
}
}
public static void Main()
{
Expression<Func<bool>> expr = () => null is Test;
Test1(new C1());
}
public static void Test1<T>(T x) where T: IDeletedID
{
Expression<Func<bool>> expr = () => x.DeletedID == 1;
Console.WriteLine(expr.Dump());
}
}";
string expectedOutput = "TypeIs(Constant(null Type:System.Object) TypeOperand:Test Type:System.Boolean)";
string expectedOutput = "Equal(MemberAccess(Convert(MemberAccess(Constant(Test+<>c__DisplayClass3_0`1[Test+C1] Type:Test+<>c__DisplayClass3_0`1[Test+C1]).x Type:Test+C1) Type:Test+IDeletedID).DeletedID Type:System.Int32) Constant(1 Type:System.Int32) Type:System.Boolean)";
CompileAndVerify(
new[] { source, ExpressionTestLibrary },
new[] { ExpressionAssemblyRef },
Expand Down

0 comments on commit af83124

Please sign in to comment.