Fix #4019: Recognize with expressions whose initializer awaits
Roslyn spills the receiver of an initializer assignment that precedes an await into a hoisted temporary, so the clone of a with expression (and the object of an object initializer) reaches the initializer transform through a copy it rejects. Only copies between two hoisted compiler temporaries are inlined: folding copies of hoisted user locals as well would turn a user's local into an initializer.
This commit is contained in:
1 parent
ef47a24511
commit
85285c1cea
4 files changed
+50
-4
No files matched your search
@@ -16,9 +16,9 @@
|
|||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
// Await shapes that still decompile to code that does not compile are tracked as #4017 (type
|
// An await shape that still decompiles to code that does not compile is tracked as #4017 (type
|
||||||
// parameter with an interface constraint), #4018 (static dynamic call) and #4019 (with
|
// parameter with an interface constraint); it is absent here because it has no correct output to
|
||||||
// expression); they are absent here because they have no correct output to pin yet.
|
// pin yet.
|
||||||
|
|
||||||
#pragma warning disable 1998
|
#pragma warning disable 1998
|
||||||
using System;
|
using System;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
#if ROSLYN2
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
|
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
|
||||||
{
|
{
|
||||||
@@ -1233,6 +1236,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
|
|||||||
Property = 42
|
Property = 42
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ROSLYN2
|
||||||
|
public static async Task<C> ObjectInitializerWithAwait(Task<int> task)
|
||||||
|
{
|
||||||
|
return new C {
|
||||||
|
Z = await task
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<C> ObjectInitializerWithAwaitAfterConstant(Task<S> task)
|
||||||
|
{
|
||||||
|
return new C {
|
||||||
|
Z = 42,
|
||||||
|
Y = await task
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Collection initializer
|
#region Collection initializer
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
#endif
|
#endif
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
|
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
|
||||||
{
|
{
|
||||||
@@ -129,6 +130,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
public async Task<PrimaryCtor> WithAwait(PrimaryCtor input)
|
||||||
|
{
|
||||||
|
return input with {
|
||||||
|
A = await Task.FromResult(42)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public async Task<Fields> WithAwaitAfterConstant(Fields input)
|
||||||
|
{
|
||||||
|
return input with {
|
||||||
|
A = 42,
|
||||||
|
B = await Task.FromResult(3.141)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract record WithNestedRecords
|
public abstract record WithNestedRecords
|
||||||
|
|||||||
@@ -668,12 +668,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|||||||
case OpCode.SwitchInstruction:
|
case OpCode.SwitchInstruction:
|
||||||
return true;
|
return true;
|
||||||
case OpCode.LdLoc:
|
case OpCode.LdLoc:
|
||||||
if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null)
|
var copiedVariable = ((LdLoc)inlinedExpression).Variable;
|
||||||
|
if (v.StateMachineField == null && copiedVariable.StateMachineField != null)
|
||||||
{
|
{
|
||||||
// Roslyn likes to put the result of fetching a state machine field into a temporary variable,
|
// Roslyn likes to put the result of fetching a state machine field into a temporary variable,
|
||||||
// so inline more aggressively in such cases.
|
// so inline more aggressively in such cases.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (IsHoistedCompilerTemporary(v) && IsHoistedCompilerTemporary(copiedVariable))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (inlinedExpression.ResultType == StackType.Ref)
|
if (inlinedExpression.ResultType == StackType.Ref)
|
||||||
@@ -773,6 +778,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsHoistedCompilerTemporary(ILVariable v)
|
||||||
|
{
|
||||||
|
string name = v.StateMachineField?.Name;
|
||||||
|
return name != null
|
||||||
|
&& (name.StartsWith("<>7__wrap", StringComparison.Ordinal) || name.StartsWith("<>s__", StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
|
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in new issue
Block a user