diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs index 962ae7121..97ab0e0a7 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/DeconstructionTests.cs @@ -10,22 +10,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { public static void Main() { - new CustomDeconstructionAndConversion().Test(); + new DeconstructionTests().Test(); } - private class CustomDeconstructionAndConversion + public struct MyInt { - public struct MyInt + public static implicit operator int(MyInt x) { - public static implicit operator int(MyInt x) - { - return 0; - } + Console.WriteLine("int op_Implicit(MyInt)"); + return 0; + } - public static implicit operator MyInt(int x) - { - return default(MyInt); - } + public static implicit operator MyInt(int x) + { + Console.WriteLine("MyInt op_Implicit(int)"); + return default(MyInt); + } + } + + private class DeconstructionSource + { + 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; @@ -35,97 +59,94 @@ 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 static MyInt? StaticNMy { - get; - set; + public int IntProperty { + get { + Console.WriteLine($"{id}.get_IntProperty()"); + return default; + } + 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 uint UIntProperty { + get { + Console.WriteLine($"{id}.get_UIntProperty()"); + return default; + } + set { + Console.WriteLine($"{id}.set_UIntProperty({value})"); + } } + } - public CustomDeconstructionAndConversion GetValue() - { - Console.WriteLine($"GetValue()"); - return new CustomDeconstructionAndConversion(); - } + private DeconstructionSource GetSource() + { + Console.WriteLine("GetSource()"); + return new DeconstructionSource(); + } - public CustomDeconstructionAndConversion Get(int i) - { - Console.WriteLine($"Get({i})"); - return new CustomDeconstructionAndConversion(); - } + private AssignmentTargets Get(int i) + { + Console.WriteLine($"Get({i})"); + return new AssignmentTargets(i); + } - private MyInt? GetNullableMyInt() - { - throw new NotImplementedException(); - } + public void Test() + { + Property_NoDeconstruction_SwappedAssignments(); + Property_NoDeconstruction_SwappedInits(); + Property_IntToUIntConversion(); + } - public void Test() - { - Property_NoDeconstruction_SwappedAssignments(); - Property_NoDeconstruction_SwappedInits(); - } + public void Property_NoDeconstruction_SwappedAssignments() + { + Console.WriteLine("Property_NoDeconstruction_SwappedAssignments:"); + AssignmentTargets customDeconstructionAndConversion = Get(0); + AssignmentTargets customDeconstructionAndConversion2 = Get(1); + GetSource().Deconstruct(out MyInt? x, out MyInt y); + MyInt myInt2 = customDeconstructionAndConversion2.My = y; + MyInt? myInt4 = customDeconstructionAndConversion.NMy = x; + } - 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); - MyInt myInt2 = customDeconstructionAndConversion2.My = y; - MyInt? myInt4 = customDeconstructionAndConversion.NMy = x; - } + public void Property_NoDeconstruction_SwappedInits() + { + Console.WriteLine("Property_NoDeconstruction_SwappedInits:"); + AssignmentTargets customDeconstructionAndConversion = Get(1); + (Get(0).NMy, customDeconstructionAndConversion.My) = GetSource(); + } - public void Property_NoDeconstruction_SwappedInits() - { - Console.WriteLine("Property_NoDeconstruction_SwappedInits:"); - CustomDeconstructionAndConversion customDeconstructionAndConversion = Get(1); - (Get(0).NMy, customDeconstructionAndConversion.My) = GetValue(); - } + public void Property_IntToUIntConversion() + { + Console.WriteLine("Property_IntToUIntConversion:"); + AssignmentTargets t0 = Get(0); + AssignmentTargets t1 = Get(1); + int a; + uint b; + GetSource().Deconstruct(out a, out b); + t0.UIntProperty = (uint)a; + t1.IntProperty = (int)b; } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs index 40c52826d..b68aa524a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs @@ -23,22 +23,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class DeconstructionTests { - private class CustomDeconstructionAndConversion + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct MyInt { - [StructLayout(LayoutKind.Sequential, Size = 1)] - public struct MyInt + public static implicit operator int(MyInt x) { - public static implicit operator int(MyInt x) - { - return 0; - } - - public static implicit operator MyInt(int x) - { - return default(MyInt); - } + return 0; } + public static implicit operator MyInt(int x) + { + return default(MyInt); + } + } + + private class DeconstructionSource + { + 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,54 +90,59 @@ 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() - { - return null; - } - - public CustomDeconstructionAndConversion Get(int i) - { - return null; - } + private DeconstructionSource GetSource() + { + return null; + } - private MyInt? GetNullableMyInt() - { - throw new NotImplementedException(); - } + private AssignmentTargets Get(int i) + { + return null; + } - public void LocalVariable_NoConversion() - { - MyInt? myInt3; - MyInt x; - (myInt3, x) = GetValue(); - Console.WriteLine(myInt3); - Console.WriteLine(x); - } + public void LocalVariable_NoConversion() + { + MyInt? myInt3; + MyInt x; + (myInt3, x) = GetSource(); + Console.WriteLine(myInt3); + Console.WriteLine(x); + } - public void LocalVariable_NoConversion_ComplexValue() - { - MyInt? myInt3; - MyInt x; - (myInt3, x) = new CustomDeconstructionAndConversion { - My = 3 - }; - Console.WriteLine(myInt3); - Console.WriteLine(x); - } + public void LocalVariable_NoConversion_ReferenceTypes() + { + string value; + string value2; + (value, value2) = GetSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + } - public void Property_NoConversion() - { - (Get(0).NMy, Get(1).My) = GetValue(); - } + public void LocalVariable_IntToLongConversion() + { + int value; + long value2; + (value, value2) = GetSource(); + Console.WriteLine(value); + Console.WriteLine(value2); + } + public void LocalVariable_NoConversion_ComplexValue() + { + MyInt? myInt3; + MyInt x; + (myInt3, x) = new DeconstructionSource { + Dummy = 3 + }; + Console.WriteLine(myInt3); + Console.WriteLine(x); + } + public void Property_NoConversion() + { + (Get(0).NMy, Get(1).My) = GetSource(); } } } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index ce10b9e90..5d692dd92 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3392,6 +3392,16 @@ namespace ICSharpCode.Decompiler.CSharp int assignmentPos = 0; var inits = inst.Init; int initPos = 0; + + Dictionary conversionMapping = new Dictionary(); + + 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 foreach (var subPattern in matchInstruction.SubPatterns.Cast()) { 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("_")); diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index c6194e378..a483d5c75 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -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(); } } diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index fbc23d51d..cb6cdb5e2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -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();")), diff --git a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs index 679c8a6f6..ad1b511a6 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs @@ -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 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 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 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)); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs index d167e4adf..23f6ae238 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs @@ -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 class DeconstructionTransform : IStatementTransform { StatementTransformContext context; - readonly Dictionary deconstructionResultsLookup = new Dictionary(); /* stloc tuple(call MakeIntIntTuple(ldloc this)) @@ -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 return; } finally { this.context = null; - this.deconstructionResultsLookup.Clear(); } } + struct ConversionInfo + { + public IType inputType; + public Conv conv; + } + /// /// stloc v(lhs) /// expr(..., deconstruct { ... }, ...) @@ -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 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 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 deconstructionResults) + bool MatchDeconstruction(ILInstruction inst, out IMethod deconstructMethod, + out ILInstruction testedOperand, out List deconstructionResults, + out Dictionary 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 if (call.Arguments.Count < 3) return false; deconstructionResults = new List(); + deconstructionResultsLookup = new Dictionary(); 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 } testedOperand = call.Arguments[0]; deconstructMethod = call.Method; - pos++; return true; } - bool MatchConversion(Block block, ref int pos) + bool MatchConversions(Block block, ref int pos, + Dictionary deconstructionResultsLookup, + out Dictionary conversions, + out List conversionStLocs) { - // TODO + conversions = new Dictionary(); + conversionStLocs = new List(); + 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 MatchAssignments(Block block, ref int pos, out List assignments) + bool MatchConversion(ILInstruction inst, out ILInstruction inputInstruction, + out ILVariable outputVariable, out ConversionInfo info) { - assignments = new List(); - int expectedIndex = 0; - while (MatchAssignment(block.Instructions.ElementAtOrDefault(pos), out var resultVariable)) { + 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, + Dictionary deconstructionResultsLookup, + Dictionary conversions, + List conversionStLocs, + ref Action delayedActions) + { + 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; - assignments.Add(block.Instructions[pos]); + 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; + } + delayedActions += addAssignment; pos++; - expectedIndex++; + previousIndex = index; + } + AddMissingAssignmentsForConversions(int.MaxValue, ref delayedActions); + return startPos != pos; + + void AddMissingAssignmentsForConversions(int index, ref Action 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 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 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; } } }