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

Expression variables declared in a local function parameter default are given a scope #16315

Closed
wants to merge 5 commits into from

Conversation

gafter
Copy link
Member

@gafter gafter commented Jan 7, 2017

Customer scenario

If you declare an expression variable (e.g. out variable) inside the default value
expression of a parameter to a local function, the IDE crashes.

Bugs this fixes:

Fixes #16167

Workarounds, if any

Don't make that mistake.

Risk

Small. Corrects a small oversight in the implementation of the interaction of new language features.

Performance impact

Trivial.

Is this a regression from a previous update?

No.

Root cause analysis:

Missing test for interaction between multiple new language features. Lack of dedicated testing team. Lack of coordinated testing role for new features and interactions between new features.

How was the bug found?

Customer reported.

@AlekseyTs @agocke @dotnet/roslyn-compiler May I please have a couple of reviews for this very small bug fix?

@gafter gafter added this to the 2.0 (RC.3) milestone Jan 7, 2017
@gafter gafter self-assigned this Jan 7, 2017
}

[Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")]
public void Bug16167()
Copy link
Member

Choose a reason for hiding this comment

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

I'd consider moving this test to another test file (with out vars or local functions maybe). It is not "random trash" ;-)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, we should stress semantic model and OutVarTests has good helpers for that. Please use the helpers.


In reply to: 95045691 [](ancestors = 95045691)

Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately, those helpers are specialized so that they do not work for pattern variables or deconstruction variables.

Copy link
Member Author

Choose a reason for hiding this comment

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

Because we do not preserve bound nodes for these contexts, the "usual" verifications we do in OutVarTests, PatternTests, etc will not pass. Fixing that will be a relatively much larger undertaking with marginal customer value. I'd prefer to keep this test as it is an file a bug for the (less important) issue of the general semantic model APIs not working well in this context.

Copy link
Member Author

Choose a reason for hiding this comment

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

Filed #16374 for these.

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM

";
// the scope of an expression variable introduced in the default expression
// of a local function parameter is that default expression.
var compilation = CreateCompilationWithMscorlib45(text);
Copy link
Contributor

Choose a reason for hiding this comment

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

compilation [](start = 16, length = 11)

Please assert diagnostics for the compilation.

{
public static void Main(int arg)
{
void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 7, 2017

Choose a reason for hiding this comment

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

{} [](start = 64, length = 2)

Please add references to the declared locals into the body of the owning local function. Here and below. We should confirm that they are not in scope, etc.

void Local4(bool b = M(arg is var z4, z4), int s1 = z4) {}
void Local5(bool b = M(M(out var z5), z5)), int s2 = z5) {}
void Local6(bool b = M(M((var z6, int a2) = (1, 2)), z6), int a3 = z6) {}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

} [](start = 4, length = 1)

Please add reference to all declared locals at the end of the method body. We should confirm that they are not in scope, etc.

{
public static void Main(int arg)
{
void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 7, 2017

Choose a reason for hiding this comment

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

z1 [](start = 60, length = 2)

We should test that locals and local functions declared within containing block are in scope inside default values. Semantic model should be tested for this as well: Lookup, GetSymbolInfo, etc.

@@ -224,6 +224,11 @@ public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax no
var oldMethod = _containingMemberOrLambda;
_containingMemberOrLambda = match;

foreach (var parameter in node.ParameterList.Parameters)
{
Visit(parameter.Default);
Copy link
Contributor

Choose a reason for hiding this comment

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

Visit(parameter.Default); [](start = 20, length = 25)

It looks like BindParameterDefaultValue would always crash without this code. This indicates that we probably do not have any tests that verify that default parameter values are handled properly for local functions. Please add some of the success tests that verify that it is possible to take advantage of the default values. Should also check if values make it to metadata.

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'll add an issue for that to be done separately.

Copy link
Member Author

Choose a reason for hiding this comment

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

Filed #16352 for this.

@gafter
Copy link
Member Author

gafter commented Jan 10, 2017

tagging @jaredpar by his request.

@@ -2159,5 +2161,163 @@ public unsafe void M2()
// await Task.Delay(2);
Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(2)").WithLocation(33, 13));
}

[Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")]
public void Bug16167()
Copy link
Member

Choose a reason for hiding this comment

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

Consider using a descriptive name, perhaps ExpressionVariablesDeclaredInDefaultValue.

@cston
Copy link
Member

cston commented Jan 10, 2017

LGTM

@gafter gafter changed the base branch from master to dev15-rc3 January 10, 2017 17:54
@gafter gafter changed the base branch from dev15-rc3 to master January 10, 2017 17:54
@@ -2159,5 +2161,163 @@ public unsafe void M2()
// await Task.Delay(2);
Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Delay(2)").WithLocation(33, 13));
}

[Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")]
public void Bug16167()
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like the PR adds any success tests. I think we should know if scenario like this is going to work correctly:

const int x =1;
void Local(int p = x) 
{
    ...
}

{
public static void Main(int arg)
{
void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

{} [](start = 64, length = 2)

Please add references to the declared locals into the body of the owning local function. Here and below. We should confirm that they are not in scope, etc.

{
public static void Main(int arg)
{
void Local1(bool b = M(arg is int z1, z1), int s1 = z1) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

M(arg is int z1, z1) [](start = 29, length = 20)

We should test that locals and local functions declared within containing block are in scope inside default values. Semantic model should be tested for this as well: Lookup, GetSymbolInfo, etc.

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.

I think we need to have more tests especially around semantic model behavior within the default values.

@@ -224,6 +224,11 @@ public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax no
var oldMethod = _containingMemberOrLambda;
_containingMemberOrLambda = match;

foreach (var parameter in node.ParameterList.Parameters)
{
Visit(parameter.Default);
Copy link
Contributor

Choose a reason for hiding this comment

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

Visit(parameter.Default); [](start = 20, length = 25)

Looking at SourceComplexParameterSymbol.MakeDefaultExpression:

var convertedExpression = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax).BindParameterDefaultValue(defaultSyntax, parameterType, diagnostics, out valueBeforeConversion);

It feels like we need to make sure that whatever we do here is consistent (for example, BinderFlags.ParameterDefaultValue flag is set appropriately). Also, SourceComplexParameterSymbol.MakeDefaultExpression is going to create its own binder instead of using the one created by LocalBinderFactory. This looks suspicious.

Copy link
Member

Choose a reason for hiding this comment

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

It does look like we have a potential inconsistency here with respect to this flag.

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've added a comment to #16451 and #16352 noting this.

Copy link
Member

Choose a reason for hiding this comment

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

@gafter this isn't something we should delay. There is an incossitsency here we need to undercstand before we can merge a fix.

Copy link
Member Author

Choose a reason for hiding this comment

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

Default expressions are never bound directly with a binder from the factory or the cache. They are always wrapped by a binder in MakeDefaultExpression before the expression is bound. So it appears to be a consistency rather than an inconsistency.

Copy link
Contributor

Choose a reason for hiding this comment

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

You make it sound like the binders that are created in LocalBinderFactory.cs are never used to bind default parameters, at the same time creating the binders avoids a crash. So, to me it looks like, we are binding default parameters with binders that differ in flags, i.e. inconsistently.

@AlekseyTs
Copy link
Contributor

Because we do not preserve bound nodes for these contexts, the "usual" verifications we do in OutVarTests, PatternTests, etc will not pass. Fixing that will be a relatively much larger undertaking with marginal customer value. I'd prefer to keep this test as it is an file a bug for the (less important) issue of the general semantic model APIs not working well in this context.

I would like to get a better understanding what exactly is not working and how. Let's sync-up offline.

@gafter
Copy link
Member Author

gafter commented Jan 12, 2017

Will create a new PR for rc3 branch

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.

VS crashes if xml is pasted in C# editor
6 participants