提交 5c602fc6 编写于 作者: V vsadov

Removed extra conversion emitted into expression trees for a lifted binary enum operator.

Fixes #5734
上级 c816547a
......@@ -414,15 +414,13 @@ private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSym
right = _bound.Default(left.Type);
}
var loweredLeft = Visit(left);
var loweredRight = Visit(right);
// Enums are handled as per their promoted underlying type
switch (opKind.OperandTypes())
{
case BinaryOperatorKind.Enum:
case BinaryOperatorKind.EnumAndUnderlying:
case BinaryOperatorKind.UnderlyingAndEnum:
case BinaryOperatorKind.Enum:
{
var enumOperand = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left;
var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType());
......@@ -431,18 +429,38 @@ private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSym
promotedType = _nullableType.Construct(promotedType);
}
loweredLeft = PromoteEnumOperand(left, loweredLeft, promotedType, isChecked);
loweredRight = PromoteEnumOperand(right, loweredRight, promotedType, isChecked);
var loweredLeft = VisitAndPromoteEnumOperand(left, promotedType, isChecked);
var loweredRight = VisitAndPromoteEnumOperand(right, promotedType, isChecked);
var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight);
return Demote(result, type, isChecked);
}
default:
return MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight);
{
var loweredLeft = Visit(left);
var loweredRight = Visit(right);
return MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight);
}
}
}
private static BoundExpression DemoteEnumOperand(BoundExpression left)
{
if (left.Kind == BoundKind.Conversion)
{
var conversion = (BoundConversion)left;
if (!conversion.ConversionKind.IsUserDefinedConversion() &&
conversion.ConversionKind.IsImplicitConversion() &&
conversion.Type.StrippedType().IsEnumType())
{
left = conversion.Operand;
}
}
return left;
}
private BoundExpression PromoteEnumOperand(BoundExpression operand, BoundExpression loweredOperand, TypeSymbol promotedType, bool isChecked)
private BoundExpression VisitAndPromoteEnumOperand(BoundExpression operand, TypeSymbol promotedType, bool isChecked)
{
var literal = operand as BoundLiteral;
if (literal != null)
......@@ -452,6 +470,11 @@ private BoundExpression PromoteEnumOperand(BoundExpression operand, BoundExpress
}
else
{
// COMPAT: if we have an operand converted to enum, we should unconvert it first
// Otherwise we will have an extra conversion in the tree: op -> enum -> underlying
// where native compiler would just directly convert to underlying
var demotedOperand = DemoteEnumOperand(operand);
var loweredOperand = Visit(demotedOperand);
return Convert(loweredOperand, operand.Type, promotedType, isChecked, false);
}
}
......
......@@ -4536,6 +4536,174 @@ public static void Main()
expectedOutput: expectedOutput);
}
[Fact, WorkItem(4471, "https://github.com/dotnet/roslyn/issues/5734")]
public void EnumEquality001()
{
string source =
@"
using System;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
enum YesNo
{
Yes,
No
}
class MyType
{
public string Name { get; set; }
public YesNo? YesNo { get; set; }
public int? Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Expression<Func<MyType, bool>> expr = (MyType x) => x.YesNo == YesNo.Yes;
Console.WriteLine(expr.Dump());
}
}
}";
string expectedOutput = "Equal(Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:System.Nullable`1[ConsoleApplication1.YesNo]) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Convert(Constant(Yes Type:ConsoleApplication1.YesNo) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Lifted Type:System.Boolean)";
CompileAndVerify(
new[] { source, ExpressionTestLibrary },
new[] { ExpressionAssemblyRef },
expectedOutput: expectedOutput);
}
[Fact, WorkItem(4471, "https://github.com/dotnet/roslyn/issues/5734")]
public void EnumEquality002()
{
string source =
@"
using System;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
enum YesNo
{
Yes,
No
}
class MyType
{
public string Name { get; set; }
public YesNo? YesNo { get; set; }
public int? Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Expression<Func<MyType, bool>> expr = (MyType x) => x.YesNo == x.YesNo;
Console.WriteLine(expr.Dump());
}
}
}";
string expectedOutput = "Equal(Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:System.Nullable`1[ConsoleApplication1.YesNo]) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:System.Nullable`1[ConsoleApplication1.YesNo]) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Lifted Type:System.Boolean)";
CompileAndVerify(
new[] { source, ExpressionTestLibrary },
new[] { ExpressionAssemblyRef },
expectedOutput: expectedOutput);
}
[Fact, WorkItem(4471, "https://github.com/dotnet/roslyn/issues/5734")]
public void EnumEquality003()
{
string source =
@"
using System;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
enum YesNo
{
Yes,
No
}
class MyType
{
public string Name { get; set; }
public YesNo YesNo { get; set; }
public int? Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Expression<Func<MyType, bool>> expr = (MyType x) => x.YesNo == x.YesNo;
Console.WriteLine(expr.Dump());
}
}
}";
string expectedOutput = "Equal(Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:ConsoleApplication1.YesNo) Type:System.Int32) Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:ConsoleApplication1.YesNo) Type:System.Int32) Type:System.Boolean)";
CompileAndVerify(
new[] { source, ExpressionTestLibrary },
new[] { ExpressionAssemblyRef },
expectedOutput: expectedOutput);
}
[Fact, WorkItem(4471, "https://github.com/dotnet/roslyn/issues/5734")]
public void EnumEquality004()
{
string source =
@"
using System;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
enum YesNo
{
Yes,
No
}
class MyType
{
public string Name { get; set; }
public YesNo? YesNo { get; set; }
public int? Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Expression<Func<MyType, bool>> expr = (MyType x) => x.YesNo == (YesNo)1;
Console.WriteLine(expr.Dump());
}
}
}";
string expectedOutput = "Equal(Convert(MemberAccess(Parameter(x Type:ConsoleApplication1.MyType).YesNo Type:System.Nullable`1[ConsoleApplication1.YesNo]) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Convert(Constant(No Type:ConsoleApplication1.YesNo) Lifted LiftedToNull Type:System.Nullable`1[System.Int32]) Lifted Type:System.Boolean)";
CompileAndVerify(
new[] { source, ExpressionTestLibrary },
new[] { ExpressionAssemblyRef },
expectedOutput: expectedOutput);
}
[WorkItem(546618, "DevDiv")]
[Fact]
public void TildeNullableEnum()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册