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 region analysis of == containing ?. #53687

Conversation

RikkiGibson
Copy link
Contributor

Related to #51463

This PR fixes region analysis of scenarios like DataFlowsOut for a?.b(out x) == /*<bind>M(out x)</bind>*/ by imitating the prior art for try/finally.

Tagging @jcouv @333fred @dotnet/roslyn-compiler


// This function is only for the specific scenario where the LHS of a binary '=='/'!=' operator is a conditional access
// and the RHS is either a constant value or is of a non-nullable value type.
void visitRightOperandWithAnyTransferFunction(BoundExpression rightOperand, ref TLocalState leftStateWhenNotNull)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This implementation is the same shape as VisitCatchBlockWithAnyTransferFunction.

https://github.com/dotnet/roslyn/blob/c12c1dd499a928d95114b55f06cd78c40552e39f/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs#L1793-L1815

The theory here is that in some dataflow analyses such as DataFlowsOut, we may "flip" the behavior of definite assignment within the /*bind*/ region, so that assigning a variable really behaves as if you "unassigned" the variable. Inside this function, we can take any "unassignments" which occurred in the RHS and propagate them to both this.State and stateWhenNotNull.

We do this by taking the "bottom" state in which all variables are assigned, and keeping it "on the side" in NonMonotonicState when we visit the right operand. After the visit, the only "unassigned" variables in the NonMonotonicState were those unassigned by the right operand, and we can simply Join those unassignments into the stateWhenNotNull.

See CondAccess_Equals_DataFlowsOut_02 for a test case which is impacted by this change.

public static void M(C? c)
{
int x = 0;
if (" + leftOperand + @" == /*<bind>*/c!.M0(x = 0)/*</bind>*/)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the test case where behavior is affected by the implementation change. Without the change, we don't include x in the DataFlowsOut set in this scenario.

@RikkiGibson RikkiGibson marked this pull request as ready for review May 26, 2021 00:14
@RikkiGibson RikkiGibson requested a review from a team as a code owner May 26, 2021 00:14
@RikkiGibson
Copy link
Contributor Author

@333fred @jcouv Please take a look when you get the chance.

@RikkiGibson
Copy link
Contributor Author

@333fred @jcouv @dotnet/roslyn-compiler Please review.

Assert.Equal("c", GetSymbolNamesJoined(dataFlowAnalysis.ReadInside));
Assert.Equal("c, x", GetSymbolNamesJoined(dataFlowAnalysis.ReadOutside));
Assert.Equal("x", GetSymbolNamesJoined(dataFlowAnalysis.WrittenInside));
Assert.Equal("c, x", GetSymbolNamesJoined(dataFlowAnalysis.WrittenOutside));
Copy link
Member

@333fred 333fred Jun 3, 2021

Choose a reason for hiding this comment

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

c

Why is c considered written anywhere in this function? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It appears that this is how parameters are handled in general. For example, the following test will pass in the main branch:

        [Fact]
        public void Test1()
        {
            var results = CompileAndAnalyzeDataFlowExpression(@"
class C
{
    static void M(int i)
    {
        System.Console.Write(/*<bind>*/i/*</bind>*/);
    }
}
");
            Assert.Equal("i", GetSymbolNamesJoined(results.WrittenOutside));
        }

My speculation is that it might be useful to model a parameter as having been "written" at the beginning of a method.

@@ -2452,6 +2451,35 @@ bool learnFromOperator(BoundBinaryOperator binary)

return false;
}

// This function is only for the specific scenario where the LHS of a binary '=='/'!=' operator is a conditional access
// and the RHS is either a constant value or is of a non-nullable value type.
Copy link
Member

@333fred 333fred Jun 3, 2021

Choose a reason for hiding this comment

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

Consider asserting these conditions. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found it prohibitively cumbersome to assert on this meaningfully, so I ended up deciding to just inline the local function at its call site.

@333fred
Copy link
Member

333fred commented Jun 3, 2021

Done review pass (commit 1). Just a couple of small questions/comments.

@RikkiGibson
Copy link
Contributor Author

@333fred I believe I've addressed all your comments. Please let me know if you have any others.

@jcouv Please review when you get a chance.

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 Thanks (iteration 2). Sorry for the wait

@jcouv jcouv self-assigned this Jun 4, 2021
@RikkiGibson
Copy link
Contributor Author

@333fred for second review

1 similar comment
@RikkiGibson
Copy link
Contributor Author

@333fred for second review

Copy link
Member

@333fred 333fred 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 2)

@RikkiGibson RikkiGibson merged commit a442936 into dotnet:features/improved-definite-assignment Jun 8, 2021
@RikkiGibson RikkiGibson deleted the ida-regions branch June 8, 2021 21:25
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.

3 participants