diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs index 33ec7b0b0..cb4c40a5a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs @@ -184,5 +184,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly Interlocked.Exchange(ref thisField, new Program()); Console.WriteLine("{0} {1}", this, thisField); } + + public void Test11() + { + int field1 = 1; + string field2 = "Hello World!"; + Console.WriteLine("{0} {1} {2}", this, field1, field2); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs index ba985d019..3caca5ed4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs @@ -202,5 +202,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly Interlocked.Exchange(ref displayClass.thisField, new Program()); Console.WriteLine("{0} {1}", this, displayClass.thisField); } + + public void Test11() + { + DisplayClass displayClass = new DisplayClass { + thisField = this, + field1 = 1, + field2 = "Hello World!" + }; + Console.WriteLine("{0} {1} {2}", displayClass.thisField, displayClass.field1, displayClass.field2); + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 1183f4f8b..ac79fa37a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -820,12 +820,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms { context.Step($"Remove initializer of {inst.Variable.Name}", inst); ILInstruction firstInlinedStore = null; + // Stores are appended after the initializer, in source order; a store that + // is dropped must not leave a gap, so the position is tracked separately + // from the loop index. + int insertionIndex = inst.ChildIndex; for (int i = 1; i < initBlock.Instructions.Count; i++) { var stobj = (StObj)initBlock.Instructions[i]; var variable = displayClass.VariablesToDeclare[(IField)((LdFlda)stobj.Target).Field.MemberDefinition]; + // A propagated field is replaced by the variable it was initialized from, + // so keeping its initializer would assign that variable to itself - and + // where the variable is 'this', the result is not even valid C#. Stores + // outside an object-initializer block are dropped in VisitStObj under the + // same condition. + if (variable.CanPropagate && variable.Initializers.Contains(stobj)) + continue; var inlinedStore = new StLoc(variable.GetOrDeclare(), stobj.Value).WithILRange(stobj); - parentBlock.Instructions.Insert(inst.ChildIndex + i, inlinedStore); + parentBlock.Instructions.Insert(++insertionIndex, inlinedStore); firstInlinedStore ??= inlinedStore; } context.EndStep(firstInlinedStore);