Skip to content

Disable 'use local function' in a few important language cases.#23136

Merged
sharwell merged 5 commits intodotnet:post-dev15.5-contribfrom
CyrusNajmabadi:useLocalFunctionCases
Nov 13, 2017
Merged

Disable 'use local function' in a few important language cases.#23136
sharwell merged 5 commits intodotnet:post-dev15.5-contribfrom
CyrusNajmabadi:useLocalFunctionCases

Conversation

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

Fixes #23118
Fixes #22672

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Tagging @dotnet/roslyn-ide @Pilchie .

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

CyrusNajmabadi commented Nov 12, 2017

Note: this fixes all of the cases from #22672 except for this one:

using System;

class Enclosing<T>
{
    delegate T MyDelegate(T t);
    static void Callee(MyDelegate d) => d(default);

    public class Class<T>
    {
        public void Caller()
        {
            MyDelegate local = x => x;
            Callee(local);
        }
    }
}

We will still offer the feature here, which will produce the broken code:

using System;

class Enclosing<T>
{
    delegate T MyDelegate(T t);
    static void Callee(MyDelegate d) => d(default);

    public class Class<T>
    {
        public void Caller()
        {
            T local(T x) => x;
            Callee(local);
        }
    }
}

The reason this is broken is that local is now referencing hte T type parameter from the containing class Class<T>, whereas before it was using hte T type parameter from Enclosing<T>. This breaks because, of course, there's no language facility to reference a specific type parameter with the same name (unlike with types, when you can do things like global::)

The reason i didn't fix this was that it seemed like a pretty unlikely case. Reusing the same type parameter name is highly discouraged in C# (the compiler even reports a warning for this). As such, most people will gives these non conflicting names, and the change will be fine.

If this is felt to be important enough to fix, it will take medium effort to fix. Specifically, the analysis code will have to see which type parameters were captured in the parameter and return type of the lambda. It will then have to see if, from the replacement location, the names of those type parameters bind back to that type parameter and not to some other type in scope (like another nested type, or a method/class type parameter, etc.).

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

@chborl Kevin asked me to look at #23118. IN the process of doing so, i ended up addressing most of the cases of #22672 since it was all in the exact same code location. I hope that's ok!

delegateType.DelegateInvokeMethod == null)
{
return;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this code was already here. i just swapped it with the complex check which now comes afterwards.

}

var convertedType = semanticModel.GetTypeInfo(nodeToCheck, cancellationToken).ConvertedType;
if (convertedType == null || !convertedType.IsDelegateType())
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i prefer this to if (convertedType?.IsDelegateType() != true).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 IsDelegateType() is an extension method which handles null internally. The null check here is unnecessary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@Pilchie Pilchie requested a review from a team November 12, 2017 23:16
@sharwell sharwell self-assigned this Nov 12, 2017
@sharwell sharwell added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Nov 12, 2017
@sharwell sharwell added this to the 15.6 milestone Nov 12, 2017
@sharwell
Copy link
Copy Markdown
Contributor

If this is felt to be important enough to fix

Let's just file this as a follow-up bug.

Copy link
Copy Markdown
Contributor

@sharwell sharwell left a comment

Choose a reason for hiding this comment

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

💡 Consider filing follow-up issues for remaining cases. They are lower priority, but often these are good candidates for new contributors since we can point them at an example of a similar feature already implemented.

}

var convertedType = semanticModel.GetTypeInfo(nodeToCheck, cancellationToken).ConvertedType;
if (convertedType == null || !convertedType.IsDelegateType())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 IsDelegateType() is an extension method which handles null internally. The null check here is unnecessary.

{
public void Caller()
{
MyDelegate [||]local = x => x;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Would be good to file a follow-up issue (feature request) to handle the case where the only member access is Invoke.

@sharwell
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi Let me know when you have seen the feedback above and I'll move it forward after you are done with any changes you wanted to make.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Have made the code change. Feel free to do what you want now :)

@sharwell sharwell changed the base branch from master to post-dev15.5-contrib November 13, 2017 00:43
@sharwell sharwell merged commit 3a34dce into dotnet:post-dev15.5-contrib Nov 13, 2017
@chborl
Copy link
Copy Markdown
Contributor

chborl commented Nov 16, 2017

Thanks, @CyrusNajmabadi :)

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Any time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants