Always fall back to PartialEq when a constant in a pattern is not recursively structural-eq

This commit is contained in:
Oli Scherer 2022-12-15 15:11:51 +00:00
parent 8d00f762dd
commit ad424e65d8
4 changed files with 104 additions and 76 deletions

View File

@ -380,7 +380,9 @@ fn compare(
); );
} }
/// Compare two `&T` values using `<T as std::compare::PartialEq>::eq` /// Compare two values using `<T as std::compare::PartialEq>::eq`.
/// If the values are already references, just call it directly, otherwise
/// take a reference to the values first and then call it.
fn non_scalar_compare( fn non_scalar_compare(
&mut self, &mut self,
block: BasicBlock, block: BasicBlock,
@ -441,12 +443,36 @@ fn non_scalar_compare(
} }
} }
let ty::Ref(_, deref_ty, _) = *ty.kind() else { match *ty.kind() {
bug!("non_scalar_compare called on non-reference type: {}", ty); ty::Ref(_, deref_ty, _) => ty = deref_ty,
}; _ => {
// non_scalar_compare called on non-reference type
let temp = self.temp(ty, source_info.span);
self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect));
let ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, ty);
let ref_temp = self.temp(ref_ty, source_info.span);
self.cfg.push_assign(
block,
source_info,
ref_temp,
Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, temp),
);
expect = Operand::Move(ref_temp);
let ref_temp = self.temp(ref_ty, source_info.span);
self.cfg.push_assign(
block,
source_info,
ref_temp,
Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, val),
);
val = ref_temp;
}
}
let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span));
let method = trait_method(self.tcx, eq_def_id, sym::eq, [deref_ty, deref_ty]); let method = trait_method(self.tcx, eq_def_id, sym::eq, [ty, ty]);
let bool_ty = self.tcx.types.bool; let bool_ty = self.tcx.types.bool;
let eq_result = self.temp(bool_ty, source_info.span); let eq_result = self.temp(bool_ty, source_info.span);

View File

@ -62,21 +62,13 @@ struct ConstToPat<'tcx> {
treat_byte_string_as_slice: bool, treat_byte_string_as_slice: bool,
} }
mod fallback_to_const_ref { /// This error type signals that we encountered a non-struct-eq situation.
#[derive(Debug)]
/// This error type signals that we encountered a non-struct-eq situation behind a reference.
/// We bubble this up in order to get back to the reference destructuring and make that emit /// We bubble this up in order to get back to the reference destructuring and make that emit
/// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq` /// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq`
/// on such patterns (since that function takes a reference) and not have to jump through any /// on such patterns (since that function takes a reference) and not have to jump through any
/// hoops to get a reference to the value. /// hoops to get a reference to the value.
pub(super) struct FallbackToConstRef(()); #[derive(Debug)]
struct FallbackToConstRef;
pub(super) fn fallback_to_const_ref(c2p: &super::ConstToPat<'_>) -> FallbackToConstRef {
assert!(c2p.behind_reference.get());
FallbackToConstRef(())
}
}
use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
impl<'tcx> ConstToPat<'tcx> { impl<'tcx> ConstToPat<'tcx> {
fn new( fn new(
@ -242,7 +234,7 @@ fn recur(
span, span,
FloatPattern, FloatPattern,
); );
PatKind::Constant { value: cv } return Err(FallbackToConstRef);
} }
ty::Adt(adt_def, _) if adt_def.is_union() => { ty::Adt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants // Matching on union fields is unsafe, we can't hide it in constants
@ -289,7 +281,7 @@ fn recur(
// Since we are behind a reference, we can just bubble the error up so we get a // Since we are behind a reference, we can just bubble the error up so we get a
// constant at reference type, making it easy to let the fallback call // constant at reference type, making it easy to let the fallback call
// `PartialEq::eq` on it. // `PartialEq::eq` on it.
return Err(fallback_to_const_ref(self)); return Err(FallbackToConstRef);
} }
ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => { ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
debug!( debug!(
@ -411,7 +403,7 @@ fn recur(
IndirectStructuralMatch { non_sm_ty: *pointee_ty }, IndirectStructuralMatch { non_sm_ty: *pointee_ty },
); );
} }
PatKind::Constant { value: cv } return Err(FallbackToConstRef);
} else { } else {
if !self.saw_const_match_error.get() { if !self.saw_const_match_error.get() {
self.saw_const_match_error.set(true); self.saw_const_match_error.set(true);
@ -439,12 +431,9 @@ fn recur(
// we fall back to a const pattern. If we do not do this, we may end up with // we fall back to a const pattern. If we do not do this, we may end up with
// a !structural-match constant that is not of reference type, which makes it // a !structural-match constant that is not of reference type, which makes it
// very hard to invoke `PartialEq::eq` on it as a fallback. // very hard to invoke `PartialEq::eq` on it as a fallback.
let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) { let subpattern = self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false)?;
Ok(subpattern) => PatKind::Deref { subpattern },
Err(_) => PatKind::Constant { value: cv },
};
self.behind_reference.set(old); self.behind_reference.set(old);
val PatKind::Deref { subpattern }
} }
} }
}, },
@ -452,7 +441,7 @@ fn recur(
PatKind::Constant { value: cv } PatKind::Constant { value: cv }
} }
ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => { ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => {
PatKind::Constant { value: cv } return Err(FallbackToConstRef);
} }
// FIXME: these can have very surprising behaviour where optimization levels or other // FIXME: these can have very surprising behaviour where optimization levels or other
// compilation choices change the runtime behaviour of the match. // compilation choices change the runtime behaviour of the match.
@ -469,7 +458,7 @@ fn recur(
PointerPattern PointerPattern
); );
} }
PatKind::Constant { value: cv } return Err(FallbackToConstRef);
} }
_ => { _ => {
self.saw_const_match_error.set(true); self.saw_const_match_error.set(true);

View File

@ -20,11 +20,12 @@ impl Eq for Bar {}
#[derive(PartialEq)] #[derive(PartialEq)]
enum Baz { enum Baz {
Baz1, Baz1,
Baz2 Baz2,
} }
impl Eq for Baz {} impl Eq for Baz {}
const BAZ: Baz = Baz::Baz1; const BAZ: Baz = Baz::Baz1;
#[rustfmt::skip]
fn main() { fn main() {
match FOO { match FOO {
FOO => {} FOO => {}
@ -124,8 +125,16 @@ fn quux(a: usize, b: usize) -> usize { a + b }
match WRAPQUUX { match WRAPQUUX {
Wrap(_) => {} Wrap(_) => {}
WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer WRAPQUUX => {}
//~^ ERROR unreachable pattern }
match WRAPQUUX {
Wrap(_) => {}
}
match WRAPQUUX {
//~^ ERROR: non-exhaustive patterns: `Wrap(_)` not covered
WRAPQUUX => {}
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
@ -138,8 +147,7 @@ enum WhoKnows<T> {
match WHOKNOWSQUUX { match WHOKNOWSQUUX {
WHOKNOWSQUUX => {} WHOKNOWSQUUX => {}
WhoKnows::Yay(_) => {} WhoKnows::Yay(_) => {}
WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer WHOKNOWSQUUX => {}
//~^ ERROR unreachable pattern
WhoKnows::Nope => {} WhoKnows::Nope => {}
} }
} }

View File

@ -1,5 +1,5 @@
error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:30:9 --> $DIR/consts-opaque.rs:31:9
| |
LL | FOO => {} LL | FOO => {}
| ^^^ | ^^^
@ -8,7 +8,7 @@ LL | FOO => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:37:9 --> $DIR/consts-opaque.rs:38:9
| |
LL | FOO_REF => {} LL | FOO_REF => {}
| ^^^^^^^ | ^^^^^^^
@ -17,7 +17,7 @@ LL | FOO_REF => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:45:9 --> $DIR/consts-opaque.rs:46:9
| |
LL | FOO_REF_REF => {} LL | FOO_REF_REF => {}
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -29,7 +29,7 @@ LL | FOO_REF_REF => {}
= note: `#[warn(indirect_structural_match)]` on by default = note: `#[warn(indirect_structural_match)]` on by default
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:53:9 --> $DIR/consts-opaque.rs:54:9
| |
LL | BAR => {} // should not be emitting unreachable warning LL | BAR => {} // should not be emitting unreachable warning
| ^^^ | ^^^
@ -38,7 +38,7 @@ LL | BAR => {} // should not be emitting unreachable warning
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:61:9 --> $DIR/consts-opaque.rs:62:9
| |
LL | BAR => {} LL | BAR => {}
| ^^^ | ^^^
@ -47,7 +47,7 @@ LL | BAR => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:70:9 --> $DIR/consts-opaque.rs:71:9
| |
LL | BAR => {} LL | BAR => {}
| ^^^ | ^^^
@ -56,7 +56,7 @@ LL | BAR => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:72:9 --> $DIR/consts-opaque.rs:73:9
| |
LL | BAR => {} // should not be emitting unreachable warning LL | BAR => {} // should not be emitting unreachable warning
| ^^^ | ^^^
@ -65,7 +65,7 @@ LL | BAR => {} // should not be emitting unreachable warning
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:80:9 --> $DIR/consts-opaque.rs:81:9
| |
LL | BAZ => {} LL | BAZ => {}
| ^^^ | ^^^
@ -74,7 +74,7 @@ LL | BAZ => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:90:9 --> $DIR/consts-opaque.rs:91:9
| |
LL | BAZ => {} LL | BAZ => {}
| ^^^ | ^^^
@ -83,7 +83,7 @@ LL | BAZ => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:97:9 --> $DIR/consts-opaque.rs:98:9
| |
LL | BAZ => {} LL | BAZ => {}
| ^^^ | ^^^
@ -92,7 +92,7 @@ LL | BAZ => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:32:9 --> $DIR/consts-opaque.rs:33:9
| |
LL | FOO => {} LL | FOO => {}
| --- matches any value | --- matches any value
@ -107,7 +107,7 @@ LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:39:9 --> $DIR/consts-opaque.rs:40:9
| |
LL | FOO_REF => {} LL | FOO_REF => {}
| ------- matches any value | ------- matches any value
@ -116,7 +116,7 @@ LL | Foo(_) => {} // should not be emitting unreachable warning
| ^^^^^^ unreachable pattern | ^^^^^^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:53:9 --> $DIR/consts-opaque.rs:54:9
| |
LL | Bar => {} LL | Bar => {}
| --- matches any value | --- matches any value
@ -124,7 +124,7 @@ LL | BAR => {} // should not be emitting unreachable warning
| ^^^ unreachable pattern | ^^^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:56:9 --> $DIR/consts-opaque.rs:57:9
| |
LL | Bar => {} LL | Bar => {}
| --- matches any value | --- matches any value
@ -133,7 +133,7 @@ LL | _ => {}
| ^ unreachable pattern | ^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:63:9 --> $DIR/consts-opaque.rs:64:9
| |
LL | BAR => {} LL | BAR => {}
| --- matches any value | --- matches any value
@ -142,7 +142,7 @@ LL | Bar => {} // should not be emitting unreachable warning
| ^^^ unreachable pattern | ^^^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:65:9 --> $DIR/consts-opaque.rs:66:9
| |
LL | BAR => {} LL | BAR => {}
| --- matches any value | --- matches any value
@ -151,7 +151,7 @@ LL | _ => {}
| ^ unreachable pattern | ^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:72:9 --> $DIR/consts-opaque.rs:73:9
| |
LL | BAR => {} LL | BAR => {}
| --- matches any value | --- matches any value
@ -160,7 +160,7 @@ LL | BAR => {} // should not be emitting unreachable warning
| ^^^ unreachable pattern | ^^^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:75:9 --> $DIR/consts-opaque.rs:76:9
| |
LL | BAR => {} LL | BAR => {}
| --- matches any value | --- matches any value
@ -169,7 +169,7 @@ LL | _ => {} // should not be emitting unreachable warning
| ^ unreachable pattern | ^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:82:9 --> $DIR/consts-opaque.rs:83:9
| |
LL | BAZ => {} LL | BAZ => {}
| --- matches any value | --- matches any value
@ -178,7 +178,7 @@ LL | Baz::Baz1 => {} // should not be emitting unreachable warning
| ^^^^^^^^^ unreachable pattern | ^^^^^^^^^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:84:9 --> $DIR/consts-opaque.rs:85:9
| |
LL | BAZ => {} LL | BAZ => {}
| --- matches any value | --- matches any value
@ -187,7 +187,7 @@ LL | _ => {}
| ^ unreachable pattern | ^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:92:9 --> $DIR/consts-opaque.rs:93:9
| |
LL | BAZ => {} LL | BAZ => {}
| --- matches any value | --- matches any value
@ -196,7 +196,7 @@ LL | _ => {}
| ^ unreachable pattern | ^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:99:9 --> $DIR/consts-opaque.rs:100:9
| |
LL | BAZ => {} LL | BAZ => {}
| --- matches any value | --- matches any value
@ -205,7 +205,7 @@ LL | Baz::Baz2 => {} // should not be emitting unreachable warning
| ^^^^^^^^^ unreachable pattern | ^^^^^^^^^ unreachable pattern
error: unreachable pattern error: unreachable pattern
--> $DIR/consts-opaque.rs:101:9 --> $DIR/consts-opaque.rs:102:9
| |
LL | BAZ => {} LL | BAZ => {}
| --- matches any value | --- matches any value
@ -213,19 +213,24 @@ LL | BAZ => {}
LL | _ => {} // should not be emitting unreachable warning LL | _ => {} // should not be emitting unreachable warning
| ^ unreachable pattern | ^ unreachable pattern
error: unreachable pattern error[E0004]: non-exhaustive patterns: `Wrap(_)` not covered
--> $DIR/consts-opaque.rs:127:9 --> $DIR/consts-opaque.rs:135:11
| |
LL | Wrap(_) => {} LL | match WRAPQUUX {
| ------- matches any value | ^^^^^^^^ pattern `Wrap(_)` not covered
LL | WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer |
| ^^^^^^^^ unreachable pattern note: `Wrap<fn(usize, usize) -> usize>` defined here
--> $DIR/consts-opaque.rs:117:12
error: unreachable pattern |
--> $DIR/consts-opaque.rs:141:9 LL | struct Wrap<T>(T);
| ^^^^
= note: the matched value is of type `Wrap<fn(usize, usize) -> usize>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ WRAPQUUX => {},
LL + Wrap(_) => todo!()
| |
LL | WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer
| ^^^^^^^^^^^^
error: aborting due to 24 previous errors; 1 warning emitted error: aborting due to 23 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0004`.