-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
use visitor for #[structural_match] check #62339
use visitor for #[structural_match] check #62339
Conversation
r? @varkor (rust_highfive has picked a reviewer for you, use r? to override) |
The way this is written might be bad for performance, since it is doing a visit of the type structure on every call to Thinking further on this, we might be able to avoid that cost by factoring the recursive traversal into a subroutine, and then doing the type traversal once, at the start of |
(I've left the WIP marker because I want to add tests that double-check the refactoring in commit 9db458f300a1f0c844071a41a547bd6294c99610 didn't break things.) |
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.
Just some nits...
@@ -1058,6 +1067,52 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { | |||
} | |||
} | |||
|
|||
fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>, |
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.
Indentation is weird here, should be:
fn search_for_adt_without_structural_match<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<&'tcx AdtDef> {
...
src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs
Outdated
Show resolved
Hide resolved
src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs
Outdated
Show resolved
Hide resolved
src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs
Outdated
Show resolved
Hide resolved
src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs
Outdated
Show resolved
Hide resolved
src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs
Outdated
Show resolved
Hide resolved
Does this handle matching on |
@matthewjasper no, it mishandles the case of The code also mishandles some other cases that I just discovered, some of which are regressions over the current master. So I need to fix that, and add corresponding regression tests.
I'll post a follow-up comment outlining the behavior on a set of cases after I finishing going through the analysis. |
Okay, that change 9db458f300a1f0c844071a41a547bd6294c99610 (to move the visit up above the recursive loop) broke (as in regressed) cases like these: struct NoDerive(i32);
impl PartialEq for NoDerive {
fn eq(&self, _: &Self) -> bool { false }
}
impl Eq for NoDerive { }
#[derive(PartialEq, Eq)]
struct WrapInline(NoDerive);
const WRAP_DIRECT_INLINE: WrapInline = WrapInline(NoDerive(0));
#[derive(PartialEq, Eq)]
struct WrapParam<T>(T);
const WRAP_DIRECT_PARAM: WrapParam<NoDerive> = WrapParam(NoDerive(0)); Independently of that change, regardless of where the visit is done, I/we also fail to correctly handle: const WRAP_INDIRECT_INLINE: & &WrapInline = & &WrapInline(NoDerive(0));
const WRAP_INDIRECT_PARAM: & &WrapParam<NoDerive> = & &WrapParam(NoDerive(0)); |
fb518f1
to
1e55df7
Compare
Okay so now I've removed commit 9db458f300a1f0c844071a41a547bd6294c99610 to avoid regressing cases that we were handling correctly before, and I've added 1e55df7a3ab2fca0ec35b884cf556af4b76d3624 because I've decided that we do need to visit the substs of an ADT (and just treat With that change in place, we handle @matthewjasper 's example correctly, as well as most of the other ones I identified. The one case that this version continues to mishandle is this (play): struct NoDerive;
impl PartialEq for NoDerive {
fn eq(&self, _: &Self) -> bool { false }
}
impl Eq for NoDerive { }
#[derive(PartialEq, Eq)]
struct WrapInline(NoDerive);
const WRAP_INLINE: & &WrapInline = & &WrapInline(NoDerive);
fn main() {
match WRAP_INLINE {
WRAP_INLINE => { println!("WRAP_INLINE matched WRAP_INLINE"); }
_ => { println!("WRAP_INLINE did not match WRAP_INLINE"); }
}
}
|
Okay, so now I've added a more complete traversal that addresses the problem posted above. But doing that then led me to ask some questions, like:
The fact that I'm asking these questions leads me to think three things:
|
r? @nikomatsakis since they wrote the original RFC. |
Turning this from an error to a warning exposes more issues; e.g. the point where we are doing this check in the compiler's control flow, namely lowering from HIR to HAIR, is a spot where for some reason we may attempt to lower the same input multiple times, and thus the warning gets issued redundantly. (The very fact that we are re-running lower on the same input seems like a worrisome thing, just in terms of compiler efficiency...) So, do I focus on making this a
|
Okay I'm finally narrowing in on a solution that I'm happy with, I hope. I am going to try to deploy this as a future-compat lint; I've filed #62411 as a tracking issue for that. |
…t have to go to trait def to double-check.
`#[structural_match]`. Outline of changes: * Recur as deeply as necessary when searching for `#[structural_match]`. * `#[structural_match]`: handle case of `const A: & &Wrap(NoDerive)` by including the fields of an ADT during traversal of input type. (We continue to not traverse the substs of an ADT, though, so that we continue to handle `PhantomData<NoDerive>` and `*NoDerive` properly.) * Refactored code to use `match` instead of `if let`. This ends up *with less* right-ward drift by moving the handling of the main *`ty::Adt` case *outside* the match. * Using lint (rather than hard error) mmeans we need to check that type is `PartialEq` to avoid ICE'ing the compiler in scneario where MIR codegen dispatches to `PartialEq::eq`. Added said check, and fatal error in that case.
732b54c
to
c7a6a5a
Compare
The regression tests explore: (direct | indirect | doubly-indirect | unsafe) x (embedded | param): where: embedded: `struct Wrapper(... NoDerive ...);` param: `struct Wrapper<X>(... X ...);` direct: `const A: Wrapper<...> = Wrapper(NoDerive);` indirect: `const A: & & Wrapper<...> = Wrapper(NoDerive)` doubly-indirect: `const A: & & Wrapper<...> = & & Wrapper(& & NoDerive)` unsafe: `const A: UnsafeWrap<...> = UnsafeWrap(std::ptr::null())`
c7a6a5a
to
02714b8
Compare
…arrays in same manner as before.
(last commit 02af3ca was result of my realization that while I had added special case code to preserve existing behavor for empty-arrays (#62336), I hadn't put in a corresponding unit test. That's fixed now.) |
My 2 cents:
|
hmm, looking at #36891, I now wonder: should we use a different name than Having said that, I nonetheless plan to let this PR land as is. We can bikeshed the names while the PR is on nightly (right?) Update: ha ha, I'm losing my marbles; the PR has already named the lint |
@bors r=nikomatsakis |
📌 Commit b0b64dd has been approved by |
…l-match-check, r=nikomatsakis use visitor for #[structural_match] check This changes the code so that we recur down the structure of a type of a const (rather than just inspecting at a shallow one or two levels) when we are looking to see if it has an ADT that did not derive `PartialEq` and `Eq`. Fix #61188 Fix #62307 Cc #62336
☀️ Test successful - checks-azure, checks-travis, status-appveyor |
This changes the code so that we recur down the structure of a type of a const (rather than just inspecting at a shallow one or two levels) when we are looking to see if it has an ADT that did not derive
PartialEq
andEq
.Fix #61188
Fix #62307
Cc #62336