From 13fa499c3547112c3c11200b7d33796c9f0c8efb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 10 Aug 2019 19:03:25 +0200 Subject: [PATCH] Fix #1630: Do not convert while to for statement, if any iterator variables are to be declared in the loop body. This causes problems with ref-typed variables. --- .../TestCases/Pretty/RefLocalsAndReturns.cs | 19 +++++++++++++++++ .../CSharp/Transforms/DeclareVariables.cs | 2 +- .../Transforms/PatternStatementTransform.cs | 21 +++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 385e0932c..63712439a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -26,6 +26,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty internal class RefLocalsAndReturns { + public struct Issue1630 + { + private object data; + + private int next; + + public static void Test() + { + Issue1630[] array = new Issue1630[1]; + int num = 0; + while (num >= 0) { + ref Issue1630 reference = ref array[num]; + Console.WriteLine(reference.data); + num = reference.next; + } + } + } + + public delegate ref T RefFunc(); public delegate ref readonly T ReadOnlyRefFunc(); public delegate ref TReturn RefFunc(T1 param1); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index f9cb5633e..c9310107a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -291,7 +291,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - bool VariableNeedsDeclaration(VariableKind kind) + internal static bool VariableNeedsDeclaration(VariableKind kind) { switch (kind) { case VariableKind.PinnedLocal: diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index df4d62112..c96419502 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Statements = { new Repeat(new AnyNode("statement")), new NamedNode( - "increment", + "iterator", new ExpressionStatement( new AssignmentExpression { Left = new Backreference("ident"), @@ -180,6 +180,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (variable != m3.Get("ident").Single().GetILVariable()) return null; WhileStatement loop = (WhileStatement)next; + // Cannot convert to for loop, if any variable that is used in the "iterator" part of the pattern, + // will be declared in the body of the while-loop. + var iteratorStatement = m3.Get("iterator").Single(); + if (IteratorVariablesDeclaredInsideLoopBody(iteratorStatement)) + return null; // Cannot convert to for loop, because that would change the semantics of the program. // continue in while jumps to the condition block. // Whereas continue in for jumps to the increment block. @@ -193,7 +198,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms forStatement.CopyAnnotationsFrom(loop); forStatement.Initializers.Add(node); forStatement.Condition = loop.Condition.Detach(); - forStatement.Iterators.Add(m3.Get("increment").Single().Detach()); + forStatement.Iterators.Add(iteratorStatement.Detach()); forStatement.EmbeddedStatement = newBody; loop.ReplaceWith(forStatement); return forStatement; @@ -216,6 +221,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return true; return false; } + + bool IteratorVariablesDeclaredInsideLoopBody(Statement iteratorStatement) + { + foreach (var id in iteratorStatement.DescendantsAndSelf.OfType()) { + var v = id.GetILVariable(); + if (v == null || !DeclareVariables.VariableNeedsDeclaration(v.Kind)) + continue; + if (declareVariables.GetDeclarationPoint(v).Parent == iteratorStatement.Parent) + return true; + } + return false; + } #endregion #region foreach