Skip to content
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

Use document with partial semantic for completion #55745

Merged
merged 4 commits into from
Aug 30, 2021

Conversation

genlu
Copy link
Member

@genlu genlu commented Aug 20, 2021

Closes #51407

@genlu genlu marked this pull request as ready for review August 23, 2021 18:42
@genlu genlu requested a review from a team as a code owner August 23, 2021 18:42
@genlu
Copy link
Member Author

genlu commented Aug 25, 2021

@sharwell @CyrusNajmabadi @jasonmalinowski Please take a look. thanks!

var usePartialSemantic = _workspace.Options.GetOption(CompletionServiceOptions.UsePartialSemanticForCompletion);
if (usePartialSemantic)
{
(document, _) = await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(false);
Copy link
Member

@jasonmalinowski jasonmalinowski Aug 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it's a bit worrisome for me that we're throwing away the SemanticModel here. If the full Compilation was available and we didn't actually do the freeze operation, the fact we've thrown away the SemanticModel could mean that theoretically the SemanticModel and Compilation could GC, and then the request for the SemanticModel has to do extra work. It won't run generators. Put another way, throwing away the SemanticModel here is potentially undoing the optimization that GetPartialSemanticModelAsync is trying to do in the first place. This would require a pretty badly timed GC though.

I'm suspicious of GetPartialSemanticModelAsync in general so maybe we should just delete it if that seems easier, but I'm happy for that to be done in another PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, I will hold on to the reference to returned model until completion providers finished their work

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and I will leave cleaning up GetPartialSemanticModelAsync to a follow up PR

var semanticModel = usePartialSemantic
? await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(false)
: await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't still want a partial semantic model in this case? Note that a "partial" model is different than the "speculative" model that the comment mentions; the speculative guarantees the tree you have will be up to date for the file, which won't run into what I think that comment is worried about.

If you really do still need the full model, then revise the comment since the comment doesn't match the code anymore.

Copy link
Member Author

@genlu genlu Aug 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The document passed in is frozen, so I'd think GetRequiredSemanticModelAsync would return partial model, right? Providers don't know if they are getting full model or partial model, so regular here is in contrast to speculative, and I believe the comment is still accurate in that sense

Dim semanticModel = If(usePartialSemantic,
Await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(False),
Await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(False))
Dim semanticModel = (Await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(False)).semanticModel
Copy link
Member

@jasonmalinowski jasonmalinowski Aug 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as C# side.
#55745 (comment)

@jasonmalinowski
Copy link
Member

@CyrusNajmabadi Any concern how this will interact with the work you did in #45565? It's news to me that we had stopped using partial semantics in completion that this PR is fixing.

@jasonmalinowski
Copy link
Member

@CyrusNajmabadi More generally, it occurred to me that the optimization the speculative semantic model is doing is technically broken in the face of source generators, if you have a generator producing new top-level types based on code written inside a method body, it's now the case that a method body edit can impact top-level semantics. I'm not sure if that's actually a real-world concern though, or at least common enough to warrant adjusting that somehow.

@@ -263,6 +280,9 @@ private CompletionProvider GetProviderByName(string providerName)
OptionSet options,
CancellationToken cancellationToken)
{
// We don't need SemanticModel here, just want keep an reference so it won't get GC'd before CompletionProviders are able to get it.
(document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to GC.KeepAlive this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting... Let me try someting.

Copy link
Member

@sharwell sharwell Aug 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this:

Suggested change
(document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false);
(document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false);
using var _ = new KeepAlive(semanticModel);

Should be easy with this helper type:

internal readonly struct KeepAlive : IDisposable
{
    private readonly object? _instance;

    public KeepAlive(object? instance)
        => _instance = instance;

    public void Dispose()
        => GC.KeepAlive(_instance);
}

Copy link
Member Author

@genlu genlu Aug 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        //     References the specified object, which makes it ineligible for garbage collection
        //     from the start of the current routine to the point where this method is called.

Based on the doc, the helper type only guarantee _instance to be GC ineligible after Dispose is called at the end of current method, right? It can still get GC'd before then, unless Dispose is inlined

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to Dispose will occur immediately before returning, which is where we want the call to KeepAlive placed.

Copy link
Member Author

@genlu genlu Aug 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the start of the current routine

Wouldn't that make current routine be Dispose instead in this case?

@genlu
Copy link
Member Author

genlu commented Aug 30, 2021

Any concern how this will interact with the work you did in #45565?

The partial semantic snapshot has different version, so we are good here. See test added in
2c3e6bb

Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this code generally needs a cleanup to be a bit more consistent with how we're acquiring documents and stuff, but this will work.

@genlu genlu merged commit 6221323 into dotnet:main Aug 30, 2021
@ghost ghost added this to the Next milestone Aug 30, 2021
@genlu genlu deleted the PartialSemanticCompletion branch August 30, 2021 18:26
@dibarbet dibarbet modified the milestones: Next, 17.0.P4 Aug 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use partial semantic model in completion providers
4 participants