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

Fix type parameter scoping for local functions #60098

Merged
merged 8 commits into from
Mar 22, 2022
Merged

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Mar 11, 2022

... and parameter scoping for lambdas.

Reviving PR #59968 to establish correct baseline for nameof(parameter) work (draft PR). Thanks @Youssef1313 for this fix (I just added more tests)
Fixes #59775

Based on implementation, the rule for type parameters is that they are in scope in return type, type parameters and parameters area (see BinderFactoryVisitor.VisitMethodDeclaration), but not the method's attribute list. For expressions that bind a type (attribute [T], typeof(T), sizeof(T), default(T)) the type parameter will be found. But for identifiers, the type parameter will be skipped outside of nameof (see IsInMethodBody/IsInsideNameOf logic in BindIdentifier).
The spec (see pointers collected in nameof(parameter) spec) does not capture those subtleties.

Problem scenario 1 (parameter scoping for lambdas, fixed by change in LambdaSymbol.cs):

class C
{
    void M()
    {
        // error CS1503: Argument 1: cannot convert from 'int' to 'string'
        var _ = void ([My(parameter)] int parameter) => throw null;
    }
    // error CS0103: The name 'parameter' does not exist in the current context
    void M2([My(parameter)] int parameter) => throw null;
}

Problem scenario 2 (type parameter scoping for local functions, fixed by change in LocalFunctionSymbol.cs):

class C
{
    void M()
    {
        local<object>();

        [My(nameof(TParameter))] // works, but why?
        void local<TParameter>() { }
    }
    
    [My(nameof(TParameter))] // error CS0103: The name 'TParameter' does not exist in the current context
    void M2<TParameter>() { }
}

Problem scenario 3 (type parameter scoping for local functions, fixed by change in SourceTypeParameterSymbol.cs):

class C
{
    void M()
    {
        local<object>();

        // error CS0103: The name 'TParameter' does not exist in the current context
        void local<[My(TParameter)] TParameter>() => throw null;
    }

    // error CS0119: 'TParameter' is a type, which is not valid in the given context
    void M2<[My(TParameter)] TParameter>() => throw null;
}

After this PR, there remains a problem with scoping of type parameters on local function (the IsInMethodBody/IsInsideNameOf filter doesn't work properly on local functions).
Filed #60110
Example:

class C
{
    void M()
    {
        local<object>(0);

        // error CS0119: 'TParameter' is a type, which is not valid in the given context
        void local<TParameter>([My(TParameter)] int i) => throw null;
    }

    // error CS0103: The name 'TParameter' does not exist in the current context
    void M2<TParameter>([My(TParameter)] int i) => throw null;
}

After this PR, there remains a problem with LookupSymbols of type parameter in nameof in certain positions. See tests in PR for details. Filed #60194

@jcouv jcouv self-assigned this Mar 11, 2022
@jcouv jcouv force-pushed the nameof-scope2 branch 2 times, most recently from 610211b to eb2a3c3 Compare March 11, 2022 08:26
@jcouv jcouv marked this pull request as ready for review March 11, 2022 17:17
@jcouv jcouv requested a review from a team as a code owner March 11, 2022 17:17
@RikkiGibson RikkiGibson self-assigned this Mar 11, 2022
@jcouv
Copy link
Member Author

jcouv commented Mar 14, 2022

@dotnet/roslyn-compiler for review. Thanks

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Mar 14, 2022

@jcouv Could you please clearly state what scoping rules do we want to have?


In reply to: 1067279846


In reply to: 1067279846

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 1)

@jcouv
Copy link
Member Author

jcouv commented Mar 15, 2022

Added the scoping rules in OP


In reply to: 1067279846

@jcouv jcouv marked this pull request as draft March 15, 2022 07:20
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Mar 15, 2022

        Binder binder,

Consider renaming this parameter as well


In reply to: 1068087746


Refers to: src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs:347 in 6ad91b0. [](commit_id = 6ad91b0, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 4)

@jcouv jcouv marked this pull request as ready for review March 16, 2022 05:13
@jcouv
Copy link
Member Author

jcouv commented Mar 17, 2022

@AlekseyTs please take another look. Added some semantic model validation and fix.
OP was updated to reflect a bug that remains in semantic model scenario after this PR (filed #60194).

@@ -356,9 +356,10 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int
if (ownerOfTypeParametersInScope != null)
{
LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier);
if ((object)function != null)
if ((object)function != null
&& LookupPosition.IsInLocalFunctionTypeParameterScope(position, function.Syntax))
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 17, 2022

Choose a reason for hiding this comment

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

&& LookupPosition.IsInLocalFunctionTypeParameterScope(position, function.Syntax)

It looks like this is a more strict check than the one we perform before assigning to ownerOfTypeParametersInScope. Consider moving this check there as a replacement for the current check. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this is a more strict check than the one we perform before assigning to ownerOfTypeParametersInScope. Consider moving this check there as a replacement for the current check.

The check was not moved (as suggested), but was duplicated.

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 5)

@jcouv jcouv requested a review from AlekseyTs March 18, 2022 01:16
@AlekseyTs
Copy link
Contributor

Done with review pass (commit 6)

void local<TParameter>([My(typeof(TParameter))] int i) => throw null;
}

void M2<TParameter>([My(typeof(TParameter))] int i) => throw null;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused by this, are we expected to find it in the attribute when the TParameter is used inside typeof, but not when it's used by itself?

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree the rules aren't quite as regular as I would have expected, as identifier lookups and type lookups (attribute type, typeof, etc) behave differently.

Here are the current rules, inferred from implementation:

the rule for type parameters is that they are in scope in return type, type parameters and parameters area (see BinderFactoryVisitor.VisitMethodDeclaration), but not the method's attribute list. For expressions that bind a type (attribute [T], typeof(T), sizeof(T), default(T)) the type parameter will be found. But for identifiers, the type parameter will be skipped outside of nameof (see IsInMethodBody/IsInsideNameOf logic in BindIdentifier).

Copy link
Contributor

Choose a reason for hiding this comment

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

To be clear, did you write that out just now or quote from somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

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

I just wrote that out (copied from OP). The spec does not capture those subtleties :-/

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 8)

@jcouv jcouv merged commit 8062809 into dotnet:main Mar 22, 2022
@jcouv jcouv deleted the nameof-scope2 branch March 22, 2022 04:09
@ghost ghost added this to the Next milestone Mar 22, 2022
333fred added a commit to 333fred/roslyn that referenced this pull request Mar 24, 2022
…red-members

* upstream/main: (50 commits)
  Fix top-level speculation (dotnet#60274)
  Skip CodeDefinitionWindows integration tests
  Avoid resizing array builder while get unaliased references (dotnet#60220)
  Fix our x86 test execution (dotnet#60323)
  [LSP] Don't adjust breakpoints when there are diagnostics in the document (dotnet#60334)
  Allow VS Mac to access a few options and a helper method (dotnet#60333)
  Add support for CheckedStatement for smart break line (dotnet#60308)
  Initialize MEF parts before switching to the UI thread (dotnet#60289)
  Don't offer convert to switch when an implicit cast is present (dotnet#60224)
  Fix
  Add basic TS LSP server (dotnet#59639)
  Fix issue when multiple different diagnostics share a single fixer
  Update dependencies from https://github.com/dotnet/arcade build 20220321.2 (dotnet#60315)
  Check for reference assembly before deciding which language to show (dotnet#60271)
  Fix XLF files that were manually edited
  Revert "Fix a typo and an inconsistency"
  Fix conversion issues in null coalescing operator (dotnet#60202)
  Fix type parameter scoping for local functions (dotnet#60098)
  Update SDK version in docs, etc. (dotnet#60299)
  Edit the breaking change doc for publishing on docs (dotnet#60260)
  ...
333fred added a commit that referenced this pull request Mar 25, 2022
* Create solution on the main thread in integration tests

* Close solution on the main thread in integration tests

* Use cached data for completion of unimported types

Even if it might be stale. the completion list would be computed with cached data and cache refresh would be done in background.

* Use cached data for completion of unimported extension methods

Even if it might be stale. the completion list would be computed with cached data and cache refresh would be done in background.

* Try to refresh import completion cache in the background whenever completion is triggered

* Use AsyncBatchingWorkQueue for background cache updates

* Fix tests

* clean up

* Fix tests

* Make sure background task to update cache always run

* Expose ForceExpandedCompletionIndexCreation option to O#

This can be used to tweak test behavior to make it deterministic

* Don't use Workspace.CurrentSolution when refreshing cache in background

* Use AsynchronousOperationListener for background workqueues

* Update src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionOptions.cs

Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>

* Pass ThreadingContext.DisposalToken to workqueue for cache refresh

* Remove namespace imports

* Remove enableRoslynAnalyzers from codespace default settings

Follow up to #60165

We don't think this is the right default as users probably don't want this unless they have at least 8 cores.

* Fix typo in Apply_namespace_matches_folder_preferences

* Remove period at the end to be consistent with other messages

* Fix inconsistency in other languages too

* Refactoring

* Ban incompatible service provider extension methods

* Assert window title before closing

* Remove outdated comment (#60275)

* Don't block on clearing the output window on the UI thread when generating a file (#60281)

* Fix issue with remove-unnecessary-cast and necessary explicit casts.

* Fix work item

* Correct change ObjectCreationExpression to ImplictObjectCreationExpression for SmartBreakline (#60249)

* Fix the initializer adding scenario

* Fix the initializer removal scenario

* Add an Assertion in debug to help if future derive type is added

* Remove the check for NewKeyword

* Option to always use default symbol servers for GTD (#60053)

* pass the listener to the constructor

* Edit the breaking change doc for publishing on docs (#60260)

Co-authored-by: Rikki Gibson <rikkigibson@gmail.com>

* Update SDK version in docs, etc. (#60299)

* Fix type parameter scoping for local functions (#60098)

Co-authored-by: Youssef1313 <youssefvictor00@gmail.com>

* Fix conversion issues in null coalescing operator (#60202)

* Revert "Fix a typo and an inconsistency"

This reverts #60238

* Fix XLF files that were manually edited

* Check for reference assembly before deciding which language to show (#60271)

* Update dependencies from https://github.com/dotnet/arcade build 20220321.2 (#60315)

[main] Update dependencies from dotnet/arcade

* Fix issue when multiple different diagnostics share a single fixer

* Add basic TS LSP server (#59639)

* Add basic TS LSP server

* Add external access layer for TS

* Include text sync handlers

* Fix build error

* Switch to explicit interface impl

* Fix namespaces

* Remove testing string

* Fix

* Don't offer convert to switch when an implicit cast is present (#60224)

* Initialize MEF parts before switching to the UI thread (#60289)

* Add support for CheckedStatement for smart break line (#60308)

* Add support for CheckedStatement

* Address feedback

* Remove a not needed import

* Allow VS Mac to access a few options and a helper method (#60333)

* [LSP] Don't adjust breakpoints when there are diagnostics in the document (#60334)

* Fix our x86 test execution (#60323)

* Fix our x86 test execution

The transition to `dotnet test` seems to have broken our unit tests that
depend on executing on an x86 architecture. This fixes that by using the
`--arch` argument to the `dotnet test` execution.

This was made more general to facilitate our future arm64 work in this
area

* Disable test failing on x64

* fix the test

* Apply suggestions from code review

Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>

* Disable test on spanish

* Test fixes

Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>

* Avoid resizing array builder while get unaliased references (#60220)

This was being resized constantly after opening Orchard Core, allocating about 2 GB. The fact that is being called this many times appears to be another bug that I'm following up on.

* Skip CodeDefinitionWindows integration tests

* Fix top-level speculation (#60274)

Co-authored-by: Youssef Victor <youssefvictor00@gmail.com>

Co-authored-by: Sam Harwell <Sam.Harwell@microsoft.com>
Co-authored-by: gel@microsoft.com <gel@microsoft.com>
Co-authored-by: Gen Lu <genlu@users.noreply.github.com>
Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>
Co-authored-by: Jonathon Marolf <jmarolf@users.noreply.github.com>
Co-authored-by: Daniel Chalmers <daniel.chalmers@outlook.com>
Co-authored-by: astroC86 <66444189+astroC86@users.noreply.github.com>
Co-authored-by: David Wengier <david.wengier@microsoft.com>
Co-authored-by: Cyrus Najmabadi <cyrusn@microsoft.com>
Co-authored-by: Shen Chen <Cosifne@users.noreply.github.com>
Co-authored-by: CyrusNajmabadi <cyrus.najmabadi@gmail.com>
Co-authored-by: msftbot[bot] <48340428+msftbot[bot]@users.noreply.github.com>
Co-authored-by: Bill Wagner <wiwagn@microsoft.com>
Co-authored-by: Rikki Gibson <rikkigibson@gmail.com>
Co-authored-by: Rikki Gibson <rigibson@microsoft.com>
Co-authored-by: Julien Couvreur <jcouv@users.noreply.github.com>
Co-authored-by: Youssef1313 <youssefvictor00@gmail.com>
Co-authored-by: dotnet-maestro[bot] <42748379+dotnet-maestro[bot]@users.noreply.github.com>
Co-authored-by: Joey Robichaud <jorobich@microsoft.com>
Co-authored-by: David Barbet <dabarbet@microsoft.com>
Co-authored-by: Jared Parsons <jaredpparsons@gmail.com>
Co-authored-by: David Kean <davkean@microsoft.com>
@allisonchou allisonchou modified the milestones: Next, 17.2.P3 Mar 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unexpected scoping on type parameter of local function
5 participants