Skip to content

Fix region analysis of == containing ?.#53687

Merged
RikkiGibson merged 2 commits intodotnet:features/improved-definite-assignmentfrom
RikkiGibson:ida-regions
Jun 8, 2021
Merged

Fix region analysis of == containing ?.#53687
RikkiGibson merged 2 commits intodotnet:features/improved-definite-assignmentfrom
RikkiGibson:ida-regions

Conversation

@RikkiGibson
Copy link
Member

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

Copy link
Member 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
Member 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
Member Author

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

@RikkiGibson
Copy link
Member 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
Member 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.

}

// 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
Member 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
Member 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
Member Author

@333fred for second review

1 similar comment
@RikkiGibson
Copy link
Member 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