-
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
Simplify and reduce amount of work performed by SourcePropertySymbolBase constructor. #49360
Conversation
…ase constructor. Closes dotnet#45489.
1 similar comment
|
||
// Due to the fact that this method is potentially reentrant, we must use a | ||
// reentrant lock to avoid deadlock and cannot assert that at this point the work | ||
// has completed (_state.HasComplete(CompletionPart.FinishPropertyEnsureSignature)). |
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.
It seems that a number of the callers of EnsureSignature
assume that the call will exit with the various fields being properly set. Most notable _lazyType
. The re-entrant nature of the method though indicates that this is not the case. For example if EnsureSignature
calls TypeWithAnnotations
then that call will possibly because the second call to EnsureSignature
will be re-entrant and won't guarantee all of the values are set.
How do we guard against this type of problem?
I think it's the type of error that would pretty much always play out in single threaded scenarios (i.e. it's not a race). So exhaustive testing should be enough. Wanted to make sure you agreed with that assertion. #Resolved
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.
How do we guard against this type of problem?
All the fields that are expected to be used in case of reentrancy are set by EnsureSignature to some preliminary values before it does anything that could cause a reentrancy. We are already using similar mechanism for method symbols.
In reply to: 525320776 [](ancestors = 525320776)
{ | ||
Debug.Assert(GetMethod is object); | ||
|
||
if (!IsStatic && SetMethod is object && !SetMethod.IsInitOnly) |
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.
Consider a pattern here to avoid the double read of SetMethod
if (!IsStatic && SetMethod is object && !SetMethod.IsInitOnly) | |
if (!IsStatic && SetMethod is { IsInitOnly: false } | |
``` #Resolved |
@@ -1349,6 +1415,11 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok | |||
GetAttributes(); | |||
break; | |||
|
|||
case CompletionPart.StartPropertyEnsureSignature: | |||
case CompletionPart.FinishPropertyEnsureSignature: | |||
EnsureSignature(); |
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.
Believe we need the following here
_state.SpinWaitComplete(CompletionPart.FinishPropertyEnsureSignature, cancellationToken);
This is necessary if the call to EnsureSignature
could call out to ForceComplete
. In that case the call to EnsureSignature
would be re-entrant here and hence wouldn't satisfy the contract of ForceComplete
.
Unsure if this is or isn't possible given the call graph of EnsureSignature
. At the least I think we should have an assert that validates the part is marked as complete after EnsureSignature
is called. #Resolved
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.
We have SpinWaitComplete after this switch.
if the call to EnsureSignature could call out to ForceComplete.
I do not think this can happen. ForceComplete is there to make sure we collected all declaration diagnostics, it is not meant to be used for any other purpose.
I will add the suggested assert.
In reply to: 525326274 [](ancestors = 525326274)
@jaredpar Addressed feedback |
{ | ||
Debug.Assert(!isExpressionBodied || !isAutoProperty); | ||
Debug.Assert(!isExpressionBodied || !hasInitializer); |
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.
Does it make sense to add this assert? Or am I misunderstanding the relationship between the properties here?
Debug.Assert(!isExplicitInterfaceImplementation || explicitInterfaceType is not null);
``` #Resolved
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.
Does it make sense to add this assert? Or am I misunderstanding the relationship between the properties here?
In error scenarios we may fail to find the type, but the property is still an explicit implementation. It is purely a syntax check
In reply to: 526216252 [](ancestors = 526216252)
src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs
Outdated
Show resolved
Hide resolved
|
||
private void EnsureSignature(DiagnosticBag diagnostics) |
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.
Believe this can only legally called from EnsureSignature()
correct? If so did you consider making this a local function there to prevent bad calls?
Not something I feel strongly about, just curious. #Resolved
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.
Believe this can only legally called from EnsureSignature() correct? If so did you consider making this a local function there to prevent bad calls?
To simplify review process I tried to keep original code as close to the original place as possible and tried to make as little as possible changes around it. Since the method is private, I do not see significant advantage in using a local function at this point.
In reply to: 526219649 [](ancestors = 526219649)
{ | ||
// Nothing to do here | ||
Debug.Assert(isAutoPropertyAccessor); |
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.
Does this assert make sense
Debug.Assert(!isExplicitInterfaceImplementation);
``` #Resolved
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.
Does this assert make sense
That would be a valid assumption, but I don't think it is worth verifying given the nature of this sealed class.
In reply to: 526239409 [](ancestors = 526239409)
Co-authored-by: Jared Parsons <jaredpparsons@gmail.com>
Looking #Resolved |
// requires that we return here even though the work is not complete. That's because | ||
// signature is processed by first populating the return type and parameters by binding | ||
// the syntax from source. Those values are visible to the same thread for the purpose | ||
// of computing which methods are implemented and overridden. But then those values |
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.
Might we be able to use signature-only symbols, rather than partially constructed symbols, to find which methods are implemented and overridden? #Resolved
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.
Might we be able to use signature-only symbols, rather than partially constructed symbols, to find which methods are implemented and overridden?
I think technically it should be possible to locate overridden/implemented property with an alternative signature representation. A signature-only symbol is one way to do that. Perhaps, if we were implementing this from scratch, we should have taken that route. At this point, however, I prefer sticking to the existing way of doing things, which has been verified to work (in the old constructor and with method symbols). Trying to use signature-only symbols, will likely require changes elsewhere, the symbol itself is getting updated in the process, the symbol itself is getting captured somewhere. For example, in diagnostics, etc.
In reply to: 527209014 [](ancestors = 527209014)
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 Thanks (iteration 8)
* upstream/master: (47 commits) Make compiler server logging explicit (dotnet#48557) [master] Update dependencies from dotnet/arcade (dotnet#48274) Handle removed project in ExternalErrorDiagnosticUpdateSource Report an error for ref-returning auto-properties declared in an interface. (dotnet#49510) Add usings on paste (dotnet#48501) Determinism test needs to use bootstrap compiler (dotnet#49483) Simplify and reduce amount of work performed by SourcePropertySymbolBase constructor. (dotnet#49360) Updates test Simplify Split Document/Workspace handler into only handling open/closed documents respectively. only report watson once. Additional usage of a PooledHashset. (dotnet#49459) Loc checkin Update src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs Preserve annotation on comment trivia when performing formatting. Validate arguments to IAsyncCompletionSource methods Determinism fixes for AnonymousTypes in VB (dotnet#49467) Collect nfw information for a crash we're seeing. Make sure to not discard text changes when no reference changes are present Create an unsafe method from a local function when necessary (dotnet#49389) ...
Closes #45489.