-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Move reporting of ERR_IteratorMustBeAsync to avoid race condition #39990
Changes from all commits
3d6644f
e5db947
10d359f
a05a32a
7a181ef
7747b7c
7505bef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,27 +13,15 @@ namespace Microsoft.CodeAnalysis.CSharp | |
/// <summary> | ||
/// A binder for a method body, which places the method's parameters in scope | ||
/// and notes if the method is an iterator method. | ||
/// Note: instances of this type can be re-used across different attempts at compiling the same method (caching by binder factory). | ||
/// </summary> | ||
internal sealed class InMethodBinder : LocalScopeBinder | ||
{ | ||
private MultiDictionary<string, ParameterSymbol> _lazyParameterMap; | ||
private readonly MethodSymbol _methodSymbol; | ||
private SmallDictionary<string, Symbol> _lazyDefinitionMap; | ||
private IteratorInfo _iteratorInfo; | ||
|
||
private class IteratorInfo | ||
{ | ||
public static readonly IteratorInfo Empty = new IteratorInfo(default, default(ImmutableArray<Diagnostic>)); | ||
|
||
public readonly TypeWithAnnotations ElementType; | ||
public readonly ImmutableArray<Diagnostic> ElementTypeDiagnostics; | ||
|
||
public IteratorInfo(TypeWithAnnotations elementType, ImmutableArray<Diagnostic> elementTypeDiagnostics) | ||
{ | ||
this.ElementType = elementType; | ||
this.ElementTypeDiagnostics = elementTypeDiagnostics; | ||
} | ||
} | ||
private TypeWithAnnotations.Boxed _iteratorElementType; | ||
private readonly static TypeWithAnnotations.Boxed SentinelElementType = new TypeWithAnnotations.Boxed(default); | ||
|
||
public InMethodBinder(MethodSymbol owner, Binder enclosing) | ||
: base(enclosing, enclosing.Flags & ~BinderFlags.AllClearedAtExecutableCodeBoundary) | ||
|
@@ -88,17 +76,17 @@ internal override bool IsInMethodBody | |
|
||
internal void MakeIterator() | ||
{ | ||
if (_iteratorInfo == null) | ||
if (_iteratorElementType == null) | ||
{ | ||
_iteratorInfo = IteratorInfo.Empty; | ||
_iteratorElementType = SentinelElementType; | ||
} | ||
} | ||
|
||
internal override bool IsDirectlyInIterator | ||
{ | ||
get | ||
{ | ||
return _iteratorInfo != null; | ||
return _iteratorElementType != null; | ||
} | ||
} | ||
|
||
|
@@ -126,7 +114,11 @@ internal override GeneratedLabelSymbol ContinueLabel | |
} | ||
} | ||
|
||
internal override TypeWithAnnotations GetIteratorElementType(YieldStatementSyntax node, DiagnosticBag diagnostics) | ||
protected override void ValidateYield(YieldStatementSyntax node, DiagnosticBag diagnostics) | ||
{ | ||
} | ||
|
||
internal override TypeWithAnnotations GetIteratorElementType() | ||
{ | ||
RefKind refKind = _methodSymbol.RefKind; | ||
TypeSymbol returnType = _methodSymbol.ReturnType; | ||
|
@@ -139,49 +131,26 @@ internal override TypeWithAnnotations GetIteratorElementType(YieldStatementSynta | |
// and deduce an iterator element type from the return type. If we didn't do this, the | ||
// TypeInfo.ConvertedType of the yield statement would always be an error type. However, we will | ||
// not mutate any state (i.e. we won't store the result). | ||
var elementType = GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, node, diagnostics).elementType; | ||
var elementType = GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocation: null, diagnostics: null); | ||
return !elementType.IsDefault ? elementType : TypeWithAnnotations.Create(CreateErrorType()); | ||
} | ||
|
||
if (_iteratorInfo == IteratorInfo.Empty) | ||
if (_iteratorElementType == SentinelElementType) | ||
{ | ||
DiagnosticBag elementTypeDiagnostics = DiagnosticBag.GetInstance(); | ||
|
||
(TypeWithAnnotations elementType, bool asyncInterface) = GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, node, elementTypeDiagnostics); | ||
|
||
Location errorLocation = _methodSymbol.Locations[0]; | ||
TypeWithAnnotations elementType = GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocation: null, diagnostics: null); | ||
if (elementType.IsDefault) | ||
{ | ||
if (refKind != RefKind.None) | ||
{ | ||
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturnRef, errorLocation, _methodSymbol); | ||
} | ||
else if (!returnType.IsErrorType()) | ||
{ | ||
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, errorLocation, _methodSymbol, returnType); | ||
} | ||
elementType = TypeWithAnnotations.Create(CreateErrorType()); | ||
} | ||
else if (asyncInterface && !_methodSymbol.IsAsync) | ||
{ | ||
Error(elementTypeDiagnostics, ErrorCode.ERR_IteratorMustBeAsync, errorLocation, _methodSymbol, returnType); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Can we get into similar trouble with errors reported above? #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so, yes. #39992 is indeed similar. I'll address in this PR. Thanks! #Resolved |
||
} | ||
|
||
var info = new IteratorInfo(elementType, elementTypeDiagnostics.ToReadOnlyAndFree()); | ||
|
||
var oldInfo = Interlocked.CompareExchange(ref _iteratorInfo, info, IteratorInfo.Empty); | ||
if (oldInfo == IteratorInfo.Empty) | ||
{ | ||
diagnostics.AddRange(_iteratorInfo.ElementTypeDiagnostics); | ||
} | ||
Interlocked.CompareExchange(ref _iteratorElementType, new TypeWithAnnotations.Boxed(elementType), SentinelElementType); | ||
} | ||
|
||
return _iteratorInfo.ElementType; | ||
return _iteratorElementType.Value; | ||
} | ||
|
||
// If an element type is found, we also return whether the interface is meant to be used with async. | ||
internal static (TypeWithAnnotations elementType, bool asyncInterface) GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, | ||
RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics) | ||
internal static TypeWithAnnotations GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, | ||
RefKind refKind, TypeSymbol returnType, Location errorLocation, DiagnosticBag diagnostics) | ||
{ | ||
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType) | ||
{ | ||
|
@@ -193,25 +162,41 @@ internal static (TypeWithAnnotations elementType, bool asyncInterface) GetIterat | |
var objectType = compilation.GetSpecialType(SpecialType.System_Object); | ||
if (diagnostics != null) | ||
{ | ||
ReportUseSiteDiagnostics(objectType, diagnostics, errorLocationNode); | ||
ReportUseSiteDiagnostics(objectType, diagnostics, errorLocation); | ||
} | ||
return (TypeWithAnnotations.Create(objectType), false); | ||
return TypeWithAnnotations.Create(objectType); | ||
|
||
case SpecialType.System_Collections_Generic_IEnumerable_T: | ||
case SpecialType.System_Collections_Generic_IEnumerator_T: | ||
return (((NamedTypeSymbol)returnType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0], false); | ||
return ((NamedTypeSymbol)returnType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]; | ||
} | ||
|
||
if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T), TypeCompareKind.ConsiderEverything2) || | ||
TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T), TypeCompareKind.ConsiderEverything2)) | ||
if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T), TypeCompareKind.ConsiderEverything) || | ||
TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T), TypeCompareKind.ConsiderEverything)) | ||
{ | ||
return (((NamedTypeSymbol)returnType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0], true); | ||
return ((NamedTypeSymbol)returnType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]; | ||
} | ||
} | ||
|
||
return default; | ||
} | ||
|
||
internal static bool IsAsyncStreamInterface(CSharpCompilation compilation, RefKind refKind, TypeSymbol returnType) | ||
{ | ||
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType) | ||
{ | ||
TypeSymbol originalDefinition = returnType.OriginalDefinition; | ||
|
||
if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T), TypeCompareKind.ConsiderEverything) || | ||
TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T), TypeCompareKind.ConsiderEverything)) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
internal override void LookupSymbolsInSingleBinder( | ||
LookupResult result, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet<DiagnosticInfo> useSiteDiagnostics) | ||
{ | ||
|
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.
📝 I didn't find any other diagnostics stored in this binder.