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

Add note when matching on tuples/ADTs containing non-exhaustive types #114397

Merged
merged 1 commit into from
Aug 25, 2023
Merged

Add note when matching on tuples/ADTs containing non-exhaustive types #114397

merged 1 commit into from
Aug 25, 2023

Conversation

sebastiantoh
Copy link
Contributor

Fixes #85222

r? @Nadrieril

@rustbot
Copy link
Collaborator

rustbot commented Aug 3, 2023

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Nadrieril (or someone else) soon.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@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. labels Aug 3, 2023
@sebastiantoh sebastiantoh marked this pull request as ready for review August 3, 2023 04:56
@rustbot
Copy link
Collaborator

rustbot commented Aug 3, 2023

Some changes might have occurred in exhaustiveness checking

cc @Nadrieril

@Nadrieril
Copy link
Member

Nadrieril commented Aug 10, 2023

Hi, thanks for tackling this!

This is a good attempt! However I think looking at the type is not the right approach. For example, in

match Some(1usize) {
	None => {}
}

we want to report that Some(_) is missing but we don't want to show the note about non-exhaustiveness. In

match Some(1usize) {
	None => {}
	Some(0) => {}
}

we do want the note. We can't tell the difference from the types alone.

The information we want is however present in the witnesses. The way it works is that a _ in the error message means a DeconstructedPat with either a Constructor::Wildcard or a Constructor::NonExhaustive. If it's Wildcard it's the common case where we don't need the note. If it's NonExhaustive it means some unwritable patterns are missing like in the second case, and we want the note.

The NonExhaustive check you removed was doing what I just describe, however it was doing it only at the top of the pattern. If we want it to work for Some(_) etc then we'll need to recursively look inside witnesses[0] for NonExhaustive constructors of the right type. Does that all makes sense?

@sebastiantoh
Copy link
Contributor Author

thanks for the feedback!

The NonExhaustive check you removed was doing what I just describe, however it was doing it only at the top of the pattern. If we want it to work for Some(_) etc then we'll need to recursively look inside witnesses[0] for NonExhaustive constructors of the right type.

I'm not sure how can I "recursively look inside witnesses[0]`.

+    debug!("witnesses={:?}", witnesses);
+    debug!("witnesses[0].ctor()={:?}", witnesses[0].ctor());
+    debug!("witnesses[0].fields()={:?}", witnesses[0].iter_fields().collect::<Vec<_>>());
+    debug!("witnesses[0].ty()={:?}", witnesses[0].ty());
    if !is_empty_match && witnesses.len() == 1 {
        report_non_exhaustive_tys(cx, scrut_ty, &mut err, &mut FxHashSet::default());
    }

Adding the above debugging code, I get the same outputs for both of the examples you listed. Specifically, it doesn't seem possible to recurse into witnesses[0].ctor() to check for Constructor::NonExhaustive

│ │ │ │ │ ├─1ms DEBUG rustc_mir_build::thir::pattern::check_match witnesses=[Some(_ : usize)]
│ │ │ │ │ ├─1ms DEBUG rustc_mir_build::thir::pattern::check_match witnesses[0].ctor()=Variant(1)
│ │ │ │ │ ├─1ms DEBUG rustc_mir_build::thir::pattern::check_match witnesses[0].fields()=[_ : usize]
│ │ │ │ │ ├─1ms DEBUG rustc_mir_build::thir::pattern::check_match witnesses[0].ty()=std::option::Option<usize>

Is it correct to say that I need to do the following:

  1. Fix witnesses[0].ctor() such that it returns Constructor::NonExhaustive instead of Constructor::Variant(1) for the second example you highlighted
  2. Update the condition for emitting the new note to be roughly as follows
if (scrut_ty.contains(cx.tcx.types.usize) || scrut_ty.contains(cx.tcx.types.isize))
        && !is_empty_match
        && witnesses.len() == 1
        && matches!(witnesses[0].ctor(), Constructor::NonExhaustive) { ... }

@Nadrieril
Copy link
Member

What you need to look into recursively is witness.fields(). Here you got witnesses[0].fields()=[_ : usize]; this _ : usize is actually a DeconstructedPat whose ctor() could be Wildcard or NonExhaustive.

To explain quickly the constructor/fields thing, the idea is that all patterns/values either look like X(A, B, C)/X { a: A, b: B } i.e. something with "inner values", or they don't. When they exist, these inner values are stored in fields(). Whatever is left is stored in ctor(). So if the pattern is Some(Ok(_)), we might get more or less:

DeconstructedPat {
	ctor: "Some",
	fields: [
		DeconstructedPat {
			ctor: "Ok",
			fields: [
				DeconstructedPat {
					ctor: "Wildcard",
					fields: [],
				}
			],
		}
	],
}

@sebastiantoh
Copy link
Contributor Author

sebastiantoh commented Aug 21, 2023

I see, thanks for the guidance. I've updated my PR. Hopefully it's correct now!

@rust-log-analyzer

This comment has been minimized.

Comment on lines +20 to +24
match (0isize, 0usize) {
//~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
(isize::MIN..=isize::MAX, 0) => (),
(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
}
Copy link
Contributor Author

@sebastiantoh sebastiantoh Aug 21, 2023

Choose a reason for hiding this comment

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

For this case, should the ... does not have a fixed maximum value note be reported for both isize and usize?

I would think it should, but my PR currently outputs the note for isize only. When I recurse into the witness of (0isize, 0usize), I get NonExhaustive for the isize, but Wildcard for the usize. Is this intentional?

Likewise for the case at https://github.com/rust-lang/rust/pull/114397/files#diff-079dd1fff457e8e3896d1365b6d8780efeadcf0b79e01928f36f4f81874213a9R54-R58

Copy link
Member

Choose a reason for hiding this comment

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

Ah yeah, this is a limitation/feature of the exhaustiveness algorithm: it reports the "first pattern it finds", so in this case it didn't bother looking into the second part of the tuple before returning the error. It seems fine to report only the first one.

@Nadrieril
Copy link
Member

That looks great! Love the thoroughness of the tests. Let's merge this

@bors r+

@bors
Copy link
Contributor

bors commented Aug 24, 2023

📌 Commit 82ce7b1 has been approved by Nadrieril

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 24, 2023
@bors
Copy link
Contributor

bors commented Aug 25, 2023

⌛ Testing commit 82ce7b1 with merge c75b6bd...

@bors
Copy link
Contributor

bors commented Aug 25, 2023

☀️ Test successful - checks-actions
Approved by: Nadrieril
Pushing c75b6bd to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Aug 25, 2023
@bors bors merged commit c75b6bd into rust-lang:master Aug 25, 2023
@rustbot rustbot added this to the 1.74.0 milestone Aug 25, 2023
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (c75b6bd): comparison URL.

Overall result: ✅ improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-2.2% [-2.2%, -2.2%] 1
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.6% [1.0%, 2.9%] 3
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-2.0% [-2.0%, -2.0%] 1
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 631.442s -> 629.758s (-0.27%)
Artifact size: 315.61 MiB -> 315.65 MiB (0.01%)

@sebastiantoh sebastiantoh deleted the issue-85222 branch August 25, 2023 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
6 participants