Skip to content

coverage: Treat each match arm as a "branch" for branch coverage - #124154

Draft
Zalathar wants to merge 4 commits into
rust-lang:mainfrom
Zalathar:branch-arms
Draft

Zalathar wants to merge 4 commits into
rust-lang:mainfrom
Zalathar:branch-arms

Conversation

@Zalathar

@Zalathar Zalathar commented Apr 19, 2024 •

Copy link
Copy Markdown
Member

This PR adds branch coverage instrumentation for individual arms in match expressions.

It seems to work well in most cases. However, the current implementation cannot handle match expressions containing or-patterns |, and therefore ignores such match expressions entirely.

(More specifically, it ignores a match expression if any of its Candidates has pre_binding_block = None.)


Right now I'm not sure whether this should be merged as-is (to provide a useful subset of functionality, and avoid bit-rot), or should be left as a draft until the or-pattern limitation can be lifted (could potentially require major rework).

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) labels Apr 19, 2024

ghost left a comment

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.

lgtm, the added tests really speak for themselves 🎉
left a small style nit

@Zalathar

ghost commented Apr 19, 2024

Copy link
Copy Markdown
Member Author

Some small style updates (diff).

@Zalathar

ghost commented Apr 19, 2024

Copy link
Copy Markdown
Member Author

I noticed that I accidentally included my let-else test as well, but instead of removing it, I decided to just add my if-let test as well. Both of these test show that the respective features don't support branch coverage yet.

(diff)

@Zalathar

ghost commented Apr 19, 2024

Copy link
Copy Markdown
Member Author

Discard match expressions early if they have <2 arms (diff).

@Zalathar
Zalathar force-pushed the branch-arms branch 2 times, most recently from 59c2385 to 356427f Compare April 20, 2024 02:30
@Zalathar

ghost commented Apr 20, 2024

Copy link
Copy Markdown
Member Author

After investigating some more, I realised that my scheme for making the false-branch expressions isn't accounting for the actual control flow around match guards.

For matches with no guards, the false-count for any arm is just the sum of the execution counts of all subsequent arms.

But for an arm with a guard, we also need to subtract the number of times that the match succeeded but the guard failed. We're currently failing to do that, which is why we get inflated counts, which then mess up all earlier arms too.

@Zalathar

ghost commented Apr 20, 2024

Copy link
Copy Markdown
Member Author

For now I've updated the implementation to skip any match expressions that have guards, so that at least we aren't producing wrong branch-coverage numbers for them (diff).

@Zalathar

ghost commented Apr 20, 2024

Copy link
Copy Markdown
Member Author

More style updates (diff).

@Zalathar

ghost commented Apr 20, 2024

Copy link
Copy Markdown
Member Author

It looks like the point where we know an arm's pre-binding block and its guard-succeeded block is just after the call to bind_pattern in lower_match_arms.

Is there a good way to forward this information to BranchInfoBuilder?

@bors

ghost commented Apr 20, 2024

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (presumably #124194) made this pull request unmergeable. Please resolve the merge conflicts.

@Zalathar

ghost commented Apr 21, 2024

Copy link
Copy Markdown
Member Author

Rebasing this on top of #123409 turned out to be very difficult (due to how the branch and MC/DC code have become interdependent), so I've filed #124217 to help relieve that tension, separately from the branch coverage changes that will be built on top of it.

@Zalathar
Zalathar force-pushed the branch-arms branch 4 times, most recently from d0dfcd8 to bc042ac Compare April 21, 2024 09:02
LL| |
LL| 1|fn never_taken() {
LL| 1| match black_box(false) {
LL| 1| _ if black_box(false) => {}

ghost Apr 25, 2024

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.

I am wondering why the pattern _ and the guard black_box(false) are in the same code region, while there are 2 branch expressions.

I would have expected the guard to have its own counter displayed underneath

   LL|      1|        _ if black_box(false) => {}
                           ^1                  ^0

@bors

ghost commented Apr 30, 2024

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (presumably #124399) made this pull request unmergeable. Please resolve the merge conflicts.

@Zalathar

ghost commented Jun 25, 2024

Copy link
Copy Markdown
Member Author

Brushed some of the cobwebs off this PR.

I still don't want to proceed without a plan for how to handle or-patterns, but at least this implementation is now a more up-to-date reference.

Zalathar added 4 commits June 26, 2024 16:25
Within the `InstrumentCoverage` pass, we now represent branches as a list of
arms, instead of a true/false pair, until we prepare the final table of
mappings to be attached to the MIR body.

(We then flatten the list into two-way branches by treating each arm as a
branch between its success block, and the total of all later arms.)

Currently all of the branches produced by MIR building are still two-way, but
this is a step towards allowing many-way branches.
@bors

ghost commented Jun 30, 2024

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (presumably #127174) made this pull request unmergeable. Please resolve the merge conflicts.

@Nadrieril

ghost commented Jul 1, 2024

Copy link
Copy Markdown
Member

#127159 should make your life easier.

@Zalathar

ghost commented Jul 1, 2024

Copy link
Copy Markdown
Member Author

I also want to add a link here to https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Details.20of.20match.20guard.20semantics/near/448161106, from which I learned that the interaction between or-patterns and guards is more complex than I had thought, because the guard might need to be evaluated multiple times per match in the general case.

@Dylan-DPC

ghost commented Aug 15, 2024

Copy link
Copy Markdown
Member

@Zalathar any updates on this? thanks

@Dylan-DPC Dylan-DPC added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 20, 2024

This branch has not been deployed

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

Labels

A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants