Browse Source

Add support for basic deconstruction conversions

pull/2119/head
Siegfried Pammer 5 years ago
parent
commit
9cb3fe3484
  1. 121
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs
  2. 61
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs
  3. 15
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 1
      ICSharpCode.Decompiler/IL/Instructions.cs
  5. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  6. 15
      ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs
  7. 213
      ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

121
ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs

@ -10,24 +10,48 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -10,24 +10,48 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
public static void Main()
{
new CustomDeconstructionAndConversion().Test();
new DeconstructionTests().Test();
}
private class CustomDeconstructionAndConversion
{
public struct MyInt
{
public static implicit operator int(MyInt x)
{
Console.WriteLine("int op_Implicit(MyInt)");
return 0;
}
public static implicit operator MyInt(int x)
{
Console.WriteLine("MyInt op_Implicit(int)");
return default(MyInt);
}
}
private class DeconstructionSource<T, T2>
{
public int Dummy {
get;
set;
}
public void Deconstruct(out T a, out T2 b)
{
Console.WriteLine("Deconstruct");
a = default(T);
b = default(T2);
}
}
private class AssignmentTargets
{
int id;
public AssignmentTargets(int id)
{
this.id = id;
}
public int IntField;
public int? NullableIntField;
@ -35,87 +59,73 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -35,87 +59,73 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public MyInt MyIntField;
public MyInt? NullableMyIntField;
private MyInt? nMy;
private MyInt my;
public int Int {
get;
set;
}
public int? NInt {
get;
set;
}
public MyInt My {
get {
Console.WriteLine("get_My");
return my;
Console.WriteLine($"{id}.get_My()");
return default;
}
set {
Console.WriteLine("set_My");
my = value;
Console.WriteLine($"{id}.set_My({value})");
}
}
public MyInt? NMy {
get {
Console.WriteLine("get_NMy");
return nMy;
Console.WriteLine($"{id}.get_NMy()");
return default;
}
set {
Console.WriteLine("set_NMy");
nMy = value;
Console.WriteLine($"{id}.set_NMy({value})");
}
}
public static MyInt StaticMy {
get;
set;
public int IntProperty {
get {
Console.WriteLine($"{id}.get_IntProperty()");
return default;
}
public static MyInt? StaticNMy {
get;
set;
set {
Console.WriteLine($"{id}.set_IntProperty({value})");
}
public void Deconstruct(out MyInt? x, out MyInt y)
{
Console.WriteLine("Deconstruct(x, y)");
x = null;
y = default(MyInt);
}
public CustomDeconstructionAndConversion GetValue()
{
Console.WriteLine($"GetValue()");
return new CustomDeconstructionAndConversion();
public uint UIntProperty {
get {
Console.WriteLine($"{id}.get_UIntProperty()");
return default;
}
set {
Console.WriteLine($"{id}.set_UIntProperty({value})");
}
}
}
public CustomDeconstructionAndConversion Get(int i)
private DeconstructionSource<T, T2> GetSource<T, T2>()
{
Console.WriteLine($"Get({i})");
return new CustomDeconstructionAndConversion();
Console.WriteLine("GetSource()");
return new DeconstructionSource<T, T2>();
}
private MyInt? GetNullableMyInt()
private AssignmentTargets Get(int i)
{
throw new NotImplementedException();
Console.WriteLine($"Get({i})");
return new AssignmentTargets(i);
}
public void Test()
{
Property_NoDeconstruction_SwappedAssignments();
Property_NoDeconstruction_SwappedInits();
Property_IntToUIntConversion();
}
public void Property_NoDeconstruction_SwappedAssignments()
{
Console.WriteLine("Property_NoDeconstruction_SwappedAssignments:");
CustomDeconstructionAndConversion customDeconstructionAndConversion = Get(0);
CustomDeconstructionAndConversion customDeconstructionAndConversion2 = Get(1);
GetValue().Deconstruct(out MyInt? x, out MyInt y);
AssignmentTargets customDeconstructionAndConversion = Get(0);
AssignmentTargets customDeconstructionAndConversion2 = Get(1);
GetSource<MyInt?, MyInt>().Deconstruct(out MyInt? x, out MyInt y);
MyInt myInt2 = customDeconstructionAndConversion2.My = y;
MyInt? myInt4 = customDeconstructionAndConversion.NMy = x;
}
@ -123,9 +133,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -123,9 +133,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public void Property_NoDeconstruction_SwappedInits()
{
Console.WriteLine("Property_NoDeconstruction_SwappedInits:");
CustomDeconstructionAndConversion customDeconstructionAndConversion = Get(1);
(Get(0).NMy, customDeconstructionAndConversion.My) = GetValue();
AssignmentTargets customDeconstructionAndConversion = Get(1);
(Get(0).NMy, customDeconstructionAndConversion.My) = GetSource<MyInt?, MyInt>();
}
public void Property_IntToUIntConversion()
{
Console.WriteLine("Property_IntToUIntConversion:");
AssignmentTargets t0 = Get(0);
AssignmentTargets t1 = Get(1);
int a;
uint b;
GetSource<int, uint>().Deconstruct(out a, out b);
t0.UIntProperty = (uint)a;
t1.IntProperty = (int)b;
}
}
}

61
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs

@ -22,8 +22,6 @@ using System.Runtime.InteropServices; @@ -22,8 +22,6 @@ using System.Runtime.InteropServices;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class DeconstructionTests
{
private class CustomDeconstructionAndConversion
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct MyInt
@ -39,6 +37,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -39,6 +37,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
private class DeconstructionSource<T, T2>
{
public int Dummy {
get;
set;
}
public void Deconstruct(out T a, out T2 b)
{
a = default(T);
b = default(T2);
}
}
private class AssignmentTargets
{
public int IntField;
public int? NullableIntField;
@ -76,43 +90,51 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -76,43 +90,51 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
get;
set;
}
public void Deconstruct(out MyInt? x, out MyInt y)
{
x = null;
y = default(MyInt);
}
public CustomDeconstructionAndConversion GetValue()
private DeconstructionSource<T, T2> GetSource<T, T2>()
{
return null;
}
public CustomDeconstructionAndConversion Get(int i)
private AssignmentTargets Get(int i)
{
return null;
}
private MyInt? GetNullableMyInt()
{
throw new NotImplementedException();
}
public void LocalVariable_NoConversion()
{
MyInt? myInt3;
MyInt x;
(myInt3, x) = GetValue();
(myInt3, x) = GetSource<MyInt?, MyInt>();
Console.WriteLine(myInt3);
Console.WriteLine(x);
}
public void LocalVariable_NoConversion_ReferenceTypes()
{
string value;
string value2;
(value, value2) = GetSource<string, string>();
Console.WriteLine(value);
Console.WriteLine(value2);
}
public void LocalVariable_IntToLongConversion()
{
int value;
long value2;
(value, value2) = GetSource<int, int>();
Console.WriteLine(value);
Console.WriteLine(value2);
}
public void LocalVariable_NoConversion_ComplexValue()
{
MyInt? myInt3;
MyInt x;
(myInt3, x) = new CustomDeconstructionAndConversion {
My = 3
(myInt3, x) = new DeconstructionSource<MyInt?, MyInt> {
Dummy = 3
};
Console.WriteLine(myInt3);
Console.WriteLine(x);
@ -120,10 +142,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -120,10 +142,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void Property_NoConversion()
{
(Get(0).NMy, Get(1).My) = GetValue();
}
(Get(0).NMy, Get(1).My) = GetSource<MyInt?, MyInt>();
}
}
}

15
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -3392,6 +3392,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3392,6 +3392,16 @@ namespace ICSharpCode.Decompiler.CSharp
int assignmentPos = 0;
var inits = inst.Init;
int initPos = 0;
Dictionary<ILVariable, ILVariable> conversionMapping = new Dictionary<ILVariable, ILVariable>();
foreach (var conv in inst.Conversions.Instructions) {
if (!DeconstructInstruction.IsConversionStLoc(conv, out var outputVariable, out var inputVariable))
continue;
conversionMapping.Add(inputVariable, outputVariable);
}
var lhs = ConstructTuple(inst.Pattern);
return new AssignmentExpression(lhs, rhs)
.WithILInstruction(inst)
@ -3403,7 +3413,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3403,7 +3413,10 @@ namespace ICSharpCode.Decompiler.CSharp
foreach (var subPattern in matchInstruction.SubPatterns.Cast<MatchInstruction>()) {
if (subPattern.IsVar) {
if (subPattern.HasDesignator) {
expr.Elements.Add(ConstructAssignmentTarget(assignments[assignmentPos], subPattern.Variable));
if (!conversionMapping.TryGetValue(subPattern.Variable, out ILVariable value)) {
value = subPattern.Variable;
}
expr.Elements.Add(ConstructAssignmentTarget(assignments[assignmentPos], value));
assignmentPos++;
} else
expr.Elements.Add(new IdentifierExpression("_"));

1
ICSharpCode.Decompiler/IL/Instructions.cs

@ -6226,7 +6226,6 @@ namespace ICSharpCode.Decompiler.IL @@ -6226,7 +6226,6 @@ namespace ICSharpCode.Decompiler.IL
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function));
Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable);
Debug.Assert(testedOperand.ResultType == StackType.O);
AdditionalInvariants();
}
}

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -339,7 +339,7 @@ @@ -339,7 +339,7 @@
CustomClassName("MatchInstruction"), HasVariableOperand("Store"), HasMethodOperand,
BoolFlag("IsDeconstructCall"), BoolFlag("CheckType"), BoolFlag("CheckNotNull"),
CustomChildren(new []{
new ChildInfo("testedOperand") { CanInlineInto = true, ExpectedTypes = new[] { "O" } },
new ChildInfo("testedOperand") { CanInlineInto = true },
new ChildInfo("subPatterns") { IsCollection = true }
}), ResultType("I4"), CustomWriteTo, SideEffect, MayThrow, ControlFlow, CustomInvariant("AdditionalInvariants();")),

15
ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs

@ -21,6 +21,8 @@ using System.Collections.Generic; @@ -21,6 +21,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
partial class DeconstructInstruction
@ -193,17 +195,18 @@ namespace ICSharpCode.Decompiler.IL @@ -193,17 +195,18 @@ namespace ICSharpCode.Decompiler.IL
case Conv conv:
input = conv.Argument;
break;
case Call { Method: { IsOperator: true, Name: "op_Implicit" }, Arguments: { Count: 1 } } call:
input = call.Arguments[0];
break;
//case Call { Method: { IsOperator: true, Name: "op_Implicit" }, Arguments: { Count: 1 } } call:
// input = call.Arguments[0];
// break;
default:
return false;
}
return input.MatchLdLoc(out inputVariable) || input.MatchLdLoca(out inputVariable);
}
internal static bool IsAssignment(ILInstruction inst, out ILInstruction value)
internal static bool IsAssignment(ILInstruction inst, out IType expectedType, out ILInstruction value)
{
expectedType = null;
value = null;
switch (inst) {
case CallInstruction call:
@ -218,9 +221,11 @@ namespace ICSharpCode.Decompiler.IL @@ -218,9 +221,11 @@ namespace ICSharpCode.Decompiler.IL
return false;
}
}
expectedType = call.Method.Parameters.Last().Type;
value = call.Arguments.Last();
return true;
case StLoc stloc:
expectedType = stloc.Variable.Type;
value = stloc.Value;
return true;
case StObj stobj:
@ -255,7 +260,7 @@ namespace ICSharpCode.Decompiler.IL @@ -255,7 +260,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(this.conversions.FinalInstruction is Nop);
foreach (var inst in assignments.Instructions) {
if (!(IsAssignment(inst, out var value) && value.MatchLdLoc(out var inputVariable)))
if (!(IsAssignment(inst, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
throw new InvalidOperationException("inst is not an assignment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
}

213
ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -31,7 +32,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,7 +32,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
class DeconstructionTransform : IStatementTransform
{
StatementTransformContext context;
readonly Dictionary<ILVariable, int> deconstructionResultsLookup = new Dictionary<ILVariable, int>();
/*
stloc tuple(call MakeIntIntTuple(ldloc this))
@ -64,7 +64,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -64,7 +64,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
try {
this.context = context;
this.deconstructionResultsLookup.Clear();
if (TransformDeconstruction(block, pos))
return;
@ -72,10 +71,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -72,10 +71,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return;
} finally {
this.context = null;
this.deconstructionResultsLookup.Clear();
}
}
struct ConversionInfo
{
public IType inputType;
public Conv conv;
}
/// <summary>
/// stloc v(lhs)
/// expr(..., deconstruct { ... }, ...)
@ -112,11 +116,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -112,11 +116,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool TransformDeconstruction(Block block, int pos)
{
int startPos = pos;
if (!MatchDeconstruction(block, ref pos, out var deconstructMethod, out var rootTestedOperand, out var deconstructionResults))
Action<DeconstructInstruction> delayedActions = null;
if (MatchDeconstruction(block.Instructions[pos], out var deconstructMethod,
out var rootTestedOperand, out var deconstructionResults,
out var deconstructionResultsLookup))
{
pos++;
}
else {
return false;
if (!MatchConversion(block, ref pos))
}
if (!MatchConversions(block, ref pos, deconstructionResultsLookup, out var conversions, out var conversionStLocs))
return false;
if (!MatchAssignments(block, ref pos, out var assignments))
if (!MatchAssignments(block, ref pos, deconstructionResultsLookup, conversions, conversionStLocs, ref delayedActions))
return false;
context.Step("Deconstruction", block.Instructions[startPos]);
DeconstructInstruction replacement = new DeconstructInstruction();
@ -133,24 +146,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -133,24 +146,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
int index = 0;
foreach (var result in deconstructionResults) {
result.Kind = VariableKind.PatternLocal;
replacement.Pattern.SubPatterns.Add(new MatchInstruction(result, new DeconstructResultInstruction(index, result.StackType, new LdLoc(rootTempVariable))));
replacement.Pattern.SubPatterns.Add(
new MatchInstruction(
result,
new DeconstructResultInstruction(index, result.StackType, new LdLoc(rootTempVariable))
)
);
index++;
}
replacement.Conversions = new Block(BlockKind.DeconstructionConversions);
TransformAssignments(replacement, assignments);
foreach (var convInst in conversionStLocs) {
replacement.Conversions.Instructions.Add(convInst);
}
replacement.Assignments = new Block(BlockKind.DeconstructionAssignments);
delayedActions?.Invoke(replacement);
block.Instructions[startPos] = replacement;
block.Instructions.RemoveRange(startPos + 1, pos - startPos - 1);
return true;
}
bool MatchDeconstruction(Block block, ref int pos, out IMethod deconstructMethod,
out ILInstruction testedOperand, out List<ILVariable> deconstructionResults)
bool MatchDeconstruction(ILInstruction inst, out IMethod deconstructMethod,
out ILInstruction testedOperand, out List<ILVariable> deconstructionResults,
out Dictionary<ILVariable, int> deconstructionResultsLookup)
{
testedOperand = null;
deconstructMethod = null;
deconstructionResults = null;
// TODO nested deconstruction / tuple deconstruction
if (!(block.Instructions[pos] is CallInstruction call))
deconstructionResultsLookup = null;
if (!(inst is CallInstruction call))
return false;
if (!MatchInstruction.IsDeconstructMethod(call.Method))
return false;
@ -159,6 +182,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -159,6 +182,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (call.Arguments.Count < 3)
return false;
deconstructionResults = new List<ILVariable>();
deconstructionResultsLookup = new Dictionary<ILVariable, int>();
for (int i = 1; i < call.Arguments.Count; i++) {
if (!call.Arguments[i].MatchLdLoca(out var v))
return false;
@ -171,65 +195,172 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -171,65 +195,172 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
testedOperand = call.Arguments[0];
deconstructMethod = call.Method;
return true;
}
bool MatchConversions(Block block, ref int pos,
Dictionary<ILVariable, int> deconstructionResultsLookup,
out Dictionary<ILVariable, ConversionInfo> conversions,
out List<StLoc> conversionStLocs)
{
conversions = new Dictionary<ILVariable, ConversionInfo>();
conversionStLocs = new List<StLoc>();
int previousIndex = -1;
while (MatchConversion(
block.Instructions.ElementAtOrDefault(pos), out var inputInstruction,
out var outputVariable, out var info))
{
if (!inputInstruction.MatchLdLoc(out var inputVariable))
return false;
if (!deconstructionResultsLookup.TryGetValue(inputVariable, out int index))
return false;
if (index <= previousIndex)
return false;
if (!(outputVariable.IsSingleDefinition && outputVariable.LoadCount == 1))
return false;
deconstructionResultsLookup.Remove(inputVariable);
deconstructionResultsLookup.Add(outputVariable, index);
conversions.Add(outputVariable, info);
conversionStLocs.Add((StLoc)block.Instructions[pos]);
pos++;
previousIndex = index;
}
return true;
}
bool MatchConversion(Block block, ref int pos)
bool MatchConversion(ILInstruction inst, out ILInstruction inputInstruction,
out ILVariable outputVariable, out ConversionInfo info)
{
// TODO
info = default;
inputInstruction = null;
if (!inst.MatchStLoc(out outputVariable, out var value))
return false;
if (!(value is Conv conv))
return false;
info = new ConversionInfo {
inputType = conv.Argument.InferType(context.TypeSystem),
conv = conv
};
inputInstruction = conv.Argument;
return true;
}
bool MatchAssignments(Block block, ref int pos, out List<ILInstruction> assignments)
bool MatchAssignments(Block block, ref int pos,
Dictionary<ILVariable, int> deconstructionResultsLookup,
Dictionary<ILVariable, ConversionInfo> conversions,
List<StLoc> conversionStLocs,
ref Action<DeconstructInstruction> delayedActions)
{
assignments = new List<ILInstruction>();
int expectedIndex = 0;
while (MatchAssignment(block.Instructions.ElementAtOrDefault(pos), out var resultVariable)) {
int previousIndex = -1;
int conversionStLocIndex = 0;
int startPos = pos;
while (MatchAssignment(block.Instructions.ElementAtOrDefault(pos), out var targetType, out var valueInst, out var addAssignment)) {
if (!valueInst.MatchLdLoc(out var resultVariable))
return false;
if (!deconstructionResultsLookup.TryGetValue(resultVariable, out int index))
return false;
if (index != expectedIndex)
if (index <= previousIndex)
return false;
AddMissingAssignmentsForConversions(index, ref delayedActions);
if (!conversions.TryGetValue(resultVariable, out var conversionInfo)) {
conversionInfo = new ConversionInfo {
inputType = resultVariable.Type
};
}
if (block.Instructions[pos].MatchStLoc(out var assignmentTarget, out _)
&& assignmentTarget.Kind == VariableKind.StackSlot
&& assignmentTarget.IsSingleDefinition
&& conversionInfo.conv == null)
{
delayedActions += _ => {
assignmentTarget.Type = conversionInfo.inputType;
};
}
else
{
if (!IsCompatibleImplicitConversion(targetType, conversionInfo))
return false;
assignments.Add(block.Instructions[pos]);
}
delayedActions += addAssignment;
pos++;
expectedIndex++;
previousIndex = index;
}
AddMissingAssignmentsForConversions(int.MaxValue, ref delayedActions);
return startPos != pos;
void AddMissingAssignmentsForConversions(int index, ref Action<DeconstructInstruction> delayedActions)
{
while (conversionStLocIndex < conversionStLocs.Count) {
var stLoc = conversionStLocs[conversionStLocIndex];
int conversionResultIndex = deconstructionResultsLookup[stLoc.Variable];
if (conversionResultIndex >= index)
break;
if (conversionResultIndex > previousIndex) {
delayedActions += (DeconstructInstruction deconstructInst) => {
var freshVar = context.Function.RegisterVariable(VariableKind.StackSlot, stLoc.Variable.Type);
deconstructInst.Assignments.Instructions.Add(new StLoc(stLoc.Variable, new LdLoc(freshVar)));
stLoc.Variable = freshVar;
};
}
conversionStLocIndex++;
}
}
return assignments.Count > 0;
}
bool MatchAssignment(ILInstruction inst, out ILVariable resultVariable)
bool MatchAssignment(ILInstruction inst, out IType targetType, out ILInstruction valueInst, out Action<DeconstructInstruction> addAssignment)
{
resultVariable = null;
addAssignment = null;
if (inst.MatchStLoc(out var v, out var value)
&& value is Block block && block.MatchInlineAssignBlock(out var call, out var valueInst)) {
if (!DeconstructInstruction.IsAssignment(call, out _))
&& value is Block block && block.MatchInlineAssignBlock(out var call, out valueInst)) {
if (!DeconstructInstruction.IsAssignment(call, out targetType, out _))
return false;
if (!(v.IsSingleDefinition && v.LoadCount == 0))
return false;
} else if (DeconstructInstruction.IsAssignment(inst, out valueInst)) {
var valueInstCopy = valueInst;
addAssignment = (DeconstructInstruction deconstructInst) => {
call.Arguments[call.Arguments.Count - 1] = valueInstCopy;
deconstructInst.Assignments.Instructions.Add(call);
};
return true;
} else if (DeconstructInstruction.IsAssignment(inst, out targetType, out valueInst)) {
// OK - use the assignment as is
addAssignment = (DeconstructInstruction deconstructInst) => {
deconstructInst.Assignments.Instructions.Add(inst);
};
return true;
} else {
return false;
}
if (!valueInst.MatchLdLoc(out resultVariable))
return false;
return true;
}
void TransformAssignments(DeconstructInstruction replacement, List<ILInstruction> assignments)
bool IsCompatibleImplicitConversion(IType targetType, ConversionInfo conversionInfo)
{
replacement.Assignments = new Block(BlockKind.DeconstructionAssignments);
foreach (var assignment in assignments) {
var transformed = assignment;
if (transformed.MatchStLoc(out _, out var value)
&& value is Block block
&& block.MatchInlineAssignBlock(out var call, out value)) {
call.Arguments[call.Arguments.Count - 1] = value;
transformed = call;
var c = CSharpConversions.Get(context.TypeSystem)
.ImplicitConversion(conversionInfo.inputType, targetType);
if (!c.IsValid)
return false;
var inputType = conversionInfo.inputType;
var conv = conversionInfo.conv;
if (c.IsIdentityConversion) {
return conv == null || conv.Kind == ConversionKind.Nop;
}
if (c.IsNumericConversion) {
switch (conv.Kind) {
case ConversionKind.IntToFloat:
return inputType.GetSign() == conv.InputSign;
case ConversionKind.FloatPrecisionChange:
return true;
case ConversionKind.SignExtend:
return inputType.GetSign() == Sign.Signed;
case ConversionKind.ZeroExtend:
return inputType.GetSign() == Sign.Unsigned;
default:
return false;
}
replacement.Assignments.Instructions.Add(transformed);
}
return false;
}
}
}

Loading…
Cancel
Save