Skip to content

Commit 8454c59

Browse files
author
Julien Couvreur
committed
Update ILVerify to honor the "async" flag
1 parent 4459d20 commit 8454c59

File tree

3 files changed

+218
-3
lines changed

3 files changed

+218
-3
lines changed

src/coreclr/tools/ILVerification/ILImporter.Verify.cs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,7 +1875,22 @@ void ImportReturn()
18751875

18761876
var declaredReturnType = _method.Signature.ReturnType;
18771877

1878-
if (declaredReturnType.IsVoid)
1878+
// For async methods, unwrap Task/ValueTask return types
1879+
TypeDesc expectedReturnType = declaredReturnType;
1880+
if (_method.IsAsync)
1881+
{
1882+
if (IsTaskOrValueTaskType(declaredReturnType, out TypeDesc unwrappedType))
1883+
{
1884+
expectedReturnType = unwrappedType;
1885+
}
1886+
else
1887+
{
1888+
// Async methods must return Task or ValueTask
1889+
VerificationError(VerifierError.StackUnexpected);
1890+
}
1891+
}
1892+
1893+
if (expectedReturnType.IsVoid)
18791894
{
18801895
Debug.Assert(_stackTop >= 0);
18811896

@@ -1891,11 +1906,49 @@ void ImportReturn()
18911906
Check(_stackTop == 1, VerifierError.ReturnEmpty);
18921907

18931908
var actualReturnType = Pop();
1894-
CheckIsAssignable(actualReturnType, StackValue.CreateFromType(declaredReturnType));
1909+
CheckIsAssignable(actualReturnType, StackValue.CreateFromType(expectedReturnType));
1910+
1911+
Check((!expectedReturnType.IsByRef && !expectedReturnType.IsByRefLike) || actualReturnType.IsPermanentHome, VerifierError.ReturnPtrToStack);
1912+
}
1913+
}
1914+
}
1915+
1916+
bool IsTaskOrValueTaskType(TypeDesc type, out TypeDesc unwrappedType)
1917+
{
1918+
unwrappedType = null;
1919+
1920+
if (type is not MetadataType metadataType)
1921+
return false;
1922+
1923+
if (!metadataType.Namespace.SequenceEqual("System.Threading.Tasks"u8))
1924+
return false;
1925+
1926+
// Check for Task (non-generic)
1927+
if (metadataType.Name.SequenceEqual("Task"u8) && !metadataType.HasInstantiation)
1928+
{
1929+
unwrappedType = _typeSystemContext.GetWellKnownType(WellKnownType.Void);
1930+
return true;
1931+
}
18951932

1896-
Check((!declaredReturnType.IsByRef && !declaredReturnType.IsByRefLike) || actualReturnType.IsPermanentHome, VerifierError.ReturnPtrToStack);
1933+
// Check for ValueTask (non-generic)
1934+
if (metadataType.Name.SequenceEqual("ValueTask"u8) && !metadataType.HasInstantiation)
1935+
{
1936+
unwrappedType = _typeSystemContext.GetWellKnownType(WellKnownType.Void);
1937+
return true;
1938+
}
1939+
1940+
// Check for Task<T> and ValueTask<T>
1941+
if (metadataType.HasInstantiation && metadataType.Instantiation.Length == 1)
1942+
{
1943+
if (metadataType.Name.SequenceEqual("Task`1"u8) ||
1944+
metadataType.Name.SequenceEqual("ValueTask`1"u8))
1945+
{
1946+
unwrappedType = metadataType.Instantiation[0];
1947+
return true;
18971948
}
18981949
}
1950+
1951+
return false;
18991952
}
19001953

19011954
void ImportFallthrough(BasicBlock next)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
.assembly extern System.Runtime
5+
{
6+
}
7+
8+
.assembly RuntimeAsyncTests
9+
{
10+
}
11+
12+
.class public auto ansi beforefieldinit RuntimeAsyncTestsType
13+
extends [System.Runtime]System.Object
14+
{
15+
// Valid async method returning Task<int> with int on stack
16+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.Task`1<int32> AsyncTaskInt_Valid() cil managed async
17+
{
18+
ldc.i4.0
19+
ret
20+
}
21+
22+
// Valid async method returning ValueTask<int> with int on stack
23+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncValueTaskInt_Valid() cil managed async
24+
{
25+
ldc.i4.0
26+
ret
27+
}
28+
29+
// Valid async method returning ValueTask<bool> with bool (i4) on stack
30+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<bool> AsyncValueTaskBool_Valid() cil managed async
31+
{
32+
ldc.i4.1
33+
ret
34+
}
35+
36+
// Valid async method returning ValueTask<object> with object on stack
37+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<object> AsyncValueTaskObject_Valid() cil managed async
38+
{
39+
ldnull
40+
ret
41+
}
42+
43+
// Invalid: async method with wrong stack state - empty stack for ValueTask<int>
44+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncEmptyStack_Invalid_ReturnMissing() cil managed async
45+
{
46+
ret
47+
}
48+
49+
// Invalid: async method with wrong type on stack
50+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncWrongType_Invalid_StackUnexpected() cil managed async
51+
{
52+
ldnull
53+
ret
54+
}
55+
56+
// Invalid: async method with extra items on stack
57+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncExtraStack_Invalid_ReturnEmpty() cil managed async
58+
{
59+
ldc.i4.0
60+
ldc.i4.1
61+
ret
62+
}
63+
64+
// Invalid: async method with ValueTask return should have empty stack
65+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask AsyncReturnsInt_Invalid_ReturnVoid() cil managed async
66+
{
67+
ldc.i4.0
68+
ret
69+
}
70+
71+
// Invalid: method with async flag but returning integer type
72+
.method public hidebysig instance int32 NonAsyncReturnType_Invalid_StackUnexpected() cil managed async
73+
{
74+
ldc.i4.0
75+
ret
76+
}
77+
78+
// Valid: async method returning Task with nothing on stack
79+
.method public hidebysig instance class [System.Runtime]System.Threading.Tasks.Task AsyncTask_Valid() cil managed async
80+
{
81+
ret
82+
}
83+
84+
// Valid: async method returning ValueTask with nothing on stack
85+
.method public hidebysig instance class [System.Runtime]System.Threading.Tasks.ValueTask AsyncValueTask_Valid() cil managed async
86+
{
87+
ret
88+
}
89+
90+
// Invalid: async method returning from try block (not allowed)
91+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncReturnFromTry_Invalid_ReturnFromTry() cil managed async
92+
{
93+
.try
94+
{
95+
ldc.i4.0
96+
ret
97+
}
98+
catch [System.Runtime]System.Object
99+
{
100+
pop
101+
leave.s lbl_ret
102+
}
103+
104+
lbl_ret:
105+
ldc.i4.1
106+
ret
107+
}
108+
109+
// Invalid: async method returning from catch block (not allowed)
110+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncReturnFromCatch_Invalid_ReturnFromHandler() cil managed async
111+
{
112+
.try
113+
{
114+
leave.s lbl_ret
115+
}
116+
catch [System.Runtime]System.Object
117+
{
118+
pop
119+
ldc.i4.0
120+
ret
121+
}
122+
123+
lbl_ret:
124+
ldc.i4.1
125+
ret
126+
}
127+
128+
// Invalid: async method returning from filter (still not allowed)
129+
.method public hidebysig instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<int32> AsyncReturnFromFilter_Invalid_ReturnFromFilter() cil managed async
130+
{
131+
.try
132+
{
133+
leave.s lbl_ret
134+
}
135+
filter
136+
{
137+
pop
138+
ldc.i4.0
139+
ret
140+
141+
endfilter
142+
}
143+
{
144+
pop
145+
leave.s lbl_ret
146+
}
147+
148+
lbl_ret:
149+
ldc.i4.1
150+
ret
151+
}
152+
153+
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
154+
{
155+
ldarg.0
156+
call instance void [System.Runtime]System.Object::.ctor()
157+
ret
158+
}
159+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2+
<Import Project="ILTests.targets" />
3+
</Project>

0 commit comments

Comments
 (0)