-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Disallow more locals of restricted type in async methods #66264
Conversation
da9a37d
to
f633766
Compare
f633766
to
d8b22e6
Compare
@dotnet/roslyn-compiler for second review. Thanks |
@@ -298,6 +298,11 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno | |||
SourceLocalSymbol local = this.IterationVariable; | |||
local.SetTypeWithAnnotations(declType); | |||
|
|||
if (CheckRestrictedTypeInAsyncMethod(this.ContainingMemberOrLambda, declType.Type, diagnostics, typeSyntax)) | |||
{ | |||
hasErrors = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting HasError without a good reason is likely to disable some useful analysis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was out of consistency with other callers of CheckRestrictedTypeInAsyncMethod
. But I agree this error doesn't prevent us from binding.
Debug.Assert(expressionOpt is not null); | ||
if (expressionOpt.Type is not null) | ||
{ | ||
CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public async Task M() | ||
{ | ||
(Span<int> s1, Span<int> s2) = new Program(); // 1, 2 | ||
var (s3, s4) = new Program(); // 3, 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"; | ||
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); | ||
comp.VerifyDiagnostics( | ||
// (8,16): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using (default(RS)) { } // 1 | ||
using (var s1 = default(RS)) { } // 2 | ||
using (RS s2 = default(RS)) { } // 3 | ||
using RS s3 = default(RS); // 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with review pass (commit 1) |
"; | ||
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); | ||
comp.VerifyDiagnostics( | ||
// (8,16): error CS9104: Values of type 'RS' cannot be used in async methods or async lambda expressions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Values of type 'RS' cannot be used in async methods or async lambda expressions.
I think that this wording is also confusing, and quite possibly plain incorrect. Values of RS can be used in an async method. For example, the following code compiles today, and I think will continue compiling with changes in this PR, but it is using value of RS
type in an async method:
https://sharplab.io/#v2:EYLgtghglgdgNAFxFANgHwAIAYAEGCMArANwCwAUBRgMx4BMe+A7BQN4U6d60YAceANhwBZABQBKDl3bkucvAE5BAOgAiAUxQQAnqPziys+ZwDi6hACUAyhLVQAzgAcA9vfUTDxzhiaeuAXylOIMYhaxwzSxtxHABeAD4cABN1ADMIAFcUBENAynIaHAAnNJx7BCKMgGMEHGs2EMKMABYcVQcXNwkcVhw8/yA===
using System.Threading.Tasks;
public class Program
{
public async Task M()
{
await Task.Delay(1);
GetRS().Dispose();
return;
}
static RS GetRS() => default;
}
public ref struct RS
{
public void Dispose() { }
}
The problem is that we cannot spill these types, not every usage requires spilling though.
So, I think the wording should be more specific to the situation. I would use something like: "A using statement resource of type 'RS' cannot be used in async methods or async lambda expressions." #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, if we want to have a more general wording, something like: "Values of type 'RS' cannot be used in async methods or async lambda expressions in this context."
Done with review pass (commit 3) |
/// </summary> | ||
internal static bool CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax) | ||
internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax, bool reportOnTemp = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (commit 4), with a parameter rename suggestion
Fixes #62747
The bug was that we allowed declaring locals for restricted type in async methods inside
foreach
. For example,foreach (Span<int> s in ...)
in async methods.Looking around, I noticed a couple other scenarios with the same issue:
using (expression)
which lowers to use a temp