-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Conversation
…re given a scope Fixes dotnet#16167
} | ||
|
||
[Fact, WorkItem(16167, "https://github.com/dotnet/roslyn/issues/16167")] | ||
public void Bug16167() |
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.
I'd consider moving this test to another test file (with out vars or local functions maybe). It is not "random trash" ;-)
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.
Yes, we should stress semantic model and OutVarTests has good helpers for that. Please use the helpers.
In reply to: 95045691 [](ancestors = 95045691)
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.
Unfortunately, those helpers are specialized so that they do not work for pattern variables or deconstruction variables.
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.
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.
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.
Filed #16374 for these.
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
"; | ||
// the scope of an expression variable introduced in the default expression | ||
// of a local function parameter is that default expression. | ||
var compilation = CreateCompilationWithMscorlib45(text); |
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.
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) {} |
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.
{} [](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) {} | ||
} |
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.
} [](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) {} |
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.
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); |
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.
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.
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.
I'll add an issue for that to be done separately.
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.
Filed #16352 for this.
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() |
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 using a descriptive name, perhaps ExpressionVariablesDeclaredInDefaultValue
.
LGTM |
@@ -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() |
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 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) {} |
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.
{} [](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) {} |
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.
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.
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.
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); |
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.
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.
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 does look like we have a potential inconsistency here with respect to this flag.
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.
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.
@gafter this isn't something we should delay. There is an incossitsency here we need to undercstand before we can merge a fix.
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.
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.
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.
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.
I would like to get a better understanding what exactly is not working and how. Let's sync-up offline. |
Will create a new PR for rc3 branch |
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?