Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/coreclr/jit/async.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,9 @@ PhaseStatus AsyncTransformation::Run()
INDEBUG(m_compiler->mostRecentlyActivePhase = PHASE_ASYNC);
VarSetOps::AssignNoCopy(m_compiler, m_compiler->compCurLife, VarSetOps::MakeEmpty(m_compiler));

// The analyses below use BlockPredsWithEH. Clear its cache in case it was used before this.
m_compiler->m_blockToEHPreds = nullptr;

// Compute locals unchanged from their default values
DefaultValueAnalysis defaultValues(m_compiler);
defaultValues.Run();
Expand Down
8 changes: 2 additions & 6 deletions src/coreclr/jit/asyncanalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,9 @@ class MutationDataFlowCallback

void MergeHandler(BasicBlock* block, BasicBlock* firstTryBlock, BasicBlock* lastTryBlock)
{
// A handler can be reached from any point in the try region.
// A local is mutated at handler entry if it was mutated at try
// entry or mutated anywhere within the try region.
for (BasicBlock* tryBlock = firstTryBlock; tryBlock != lastTryBlock->Next(); tryBlock = tryBlock->Next())
for (FlowEdge* pred = m_compiler->BlockPredsWithEH(block); pred != nullptr; pred = pred->getNextPredEdge())
{
VarSetOps::UnionD(m_compiler, m_mutatedVarsIn[block->bbNum], m_mutatedVarsIn[tryBlock->bbNum]);
VarSetOps::UnionD(m_compiler, m_mutatedVarsIn[block->bbNum], m_mutatedVars[tryBlock->bbNum]);
Merge(block, pred->getSourceBlock(), pred->getDupCount());
Comment on lines 39 to +43
}
}

Expand Down
87 changes: 87 additions & 0 deletions src/tests/async/regression/132015.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit;

public class Runtime_132015
{
[Fact]
public static void AssignInFilterIsVisibleAfterSuspension()
{
Assert.Equal("success", AssignInFilter().GetAwaiter().GetResult());
}

[Fact]
public static void AssignInNestedFinallyIsVisibleAfterSuspension()
{
Assert.Equal("success", AssignInNestedFinally().GetAwaiter().GetResult());
Assert.Equal("success", AssignInNestedFinallyWithFilter().GetAwaiter().GetResult());
}

// The filter assigns the exception variable, which must survive the
// suspension inside the handler.
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<string> AssignInFilter()
{
try
{
throw new Exception("thrown");
}
catch (Exception caught) when ((caught = new Exception("success")) != null)
{
await Task.Yield();
return caught.Message;
}
}

// The nested finally runs during the second pass, before the handler is
// entered, so its assignment must survive the suspension in the handler.
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<string> AssignInNestedFinally()
{
string s = null;
try
{
try
{
throw new Exception("thrown");
}
finally
{
s = "success";
}
}
catch (Exception)
{
await Task.Yield();
return s ?? "<null>";
}
}

// Same as above, but a filter runs before the nested finally, so the
// handler must not assume the value that the filter observed.
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<string> AssignInNestedFinallyWithFilter()
{
string s = null;
try
{
try
{
throw new Exception("thrown");
}
finally
{
s = "success";
}
}
catch (Exception) when (s is null)
{
await Task.Yield();
return s ?? "<null>";
}
}
}
5 changes: 5 additions & 0 deletions src/tests/async/regression/132015.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>
72 changes: 72 additions & 0 deletions src/tests/async/regression/132016.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit;

public class Runtime_132016
{
private static readonly List<string> s_log = new List<string>();

[Fact]
public static void TestEntryPoint()
{
s_log.Clear();
new Runtime_132016().M().GetAwaiter().GetResult();

Assert.Equal(new[] { "outer", "outer", "inner", "inner", "outer", "inner" }, s_log);
}

private static bool Filter(Exception ex, bool result)
{
s_log.Add(ex.Message);
return result;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private async Task M()
{
try
{
throw new Exception("outer");
}
catch (Exception ex) when (Filter(ex, false))
{
await Print(ex);
}
catch (Exception ex) when (Filter(ex, true))
{
try
{
throw new Exception("inner");
}
catch (Exception ex2) when (Filter(ex2, false))
{
await Print(ex);
await Print(ex2);
}
catch (Exception ex2) when (Filter(ex2, true))
{
try
{
throw new Exception();
}
catch
{
await Print(ex);
await Print(ex2);
}
}
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task Print(Exception ex)
{
await Task.Yield();
s_log.Add(ex.Message);
}
}
5 changes: 5 additions & 0 deletions src/tests/async/regression/132016.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>
74 changes: 74 additions & 0 deletions src/tests/async/regression/132017.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit;

public class Runtime_132017
{
private static readonly List<string> s_log = new List<string>();
private static int s_calls;

// The nested catch starts this task without awaiting it; keep it around so
// that the test can wait for it deterministically.
private static Task s_pending;

[Fact]
public static void TestEntryPoint()
{
s_log.Clear();
s_calls = 0;
s_pending = null;

var test = new Runtime_132017();
test.M(false).GetAwaiter().GetResult();
test.M(true).GetAwaiter().GetResult();
s_pending.GetAwaiter().GetResult();
Comment on lines +27 to +29

Assert.Equal(new[] { "filter", "filter", "outer", "filter", "outer" }, s_log);
Assert.Equal(2, s_calls);
}

private static bool Filter(bool result)
{
s_log.Add("filter");
return result;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private async Task M(bool first)
{
try
{
throw new Exception("outer");
}
catch (Exception ex) when (Filter(first))
{
await Print(ex);
}
catch (Exception ex) when (Filter(true))
{
try
{
await Task.Yield();
throw new Exception();
}
catch
{
s_pending = Print(ex);
}
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task Print(Exception ex)
{
string message = ex.Message;
s_log.Add(message);
await Task.Yield();
s_calls++;
}
}
5 changes: 5 additions & 0 deletions src/tests/async/regression/132017.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>
Loading