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(
&mut self,
block: BasicBlock,
@ -441,12 +443,36 @@ fn non_scalar_compare(
}
}
let ty::Ref(_, deref_ty, _) = *ty.kind() else {
bug!("non_scalar_compare called on non-reference type: {}", ty);
};
match *ty.kind() {
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 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 eq_result = self.temp(bool_ty, source_info.span);

View file

@ -62,21 +62,13 @@ struct ConstToPat<'tcx> {
treat_byte_string_as_slice: bool,
}
mod fallback_to_const_ref {
#[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
/// 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
/// hoops to get a reference to the value.
pub(super) 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};
/// This error type signals that we encountered a non-struct-eq situation.
/// 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`
/// on such patterns (since that function takes a reference) and not have to jump through any
/// hoops to get a reference to the value.
#[derive(Debug)]
struct FallbackToConstRef;
impl<'tcx> ConstToPat<'tcx> {
fn new(
@ -236,13 +228,13 @@ fn recur(
let kind = match cv.ty().kind() {
ty::Float(_) => {
tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
FloatPattern,
);
PatKind::Constant { value: cv }
tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
FloatPattern,
);
return Err(FallbackToConstRef);
}
ty::Adt(adt_def, _) if adt_def.is_union() => {
// 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
// constant at reference type, making it easy to let the fallback call
// `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()) => {
debug!(
@ -411,7 +403,7 @@ fn recur(
IndirectStructuralMatch { non_sm_ty: *pointee_ty },
);
}
PatKind::Constant { value: cv }
return Err(FallbackToConstRef);
} else {
if !self.saw_const_match_error.get() {
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
// a !structural-match constant that is not of reference type, which makes it
// 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) {
Ok(subpattern) => PatKind::Deref { subpattern },
Err(_) => PatKind::Constant { value: cv },
};
let subpattern = self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false)?;
self.behind_reference.set(old);
val
PatKind::Deref { subpattern }
}
}
},
@ -452,7 +441,7 @@ fn recur(
PatKind::Constant { value: cv }
}
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
// compilation choices change the runtime behaviour of the match.
@ -469,7 +458,7 @@ fn recur(
PointerPattern
);
}
PatKind::Constant { value: cv }
return Err(FallbackToConstRef);
}
_ => {
self.saw_const_match_error.set(true);

View file

@ -20,11 +20,12 @@ impl Eq for Bar {}
#[derive(PartialEq)]
enum Baz {
Baz1,
Baz2
Baz2,
}
impl Eq for Baz {}
const BAZ: Baz = Baz::Baz1;
#[rustfmt::skip]
fn main() {
match FOO {
FOO => {}
@ -124,8 +125,16 @@ fn quux(a: usize, b: usize) -> usize { a + b }
match WRAPQUUX {
Wrap(_) => {}
WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
//~^ ERROR unreachable pattern
WRAPQUUX => {}
}
match WRAPQUUX {
Wrap(_) => {}
}
match WRAPQUUX {
//~^ ERROR: non-exhaustive patterns: `Wrap(_)` not covered
WRAPQUUX => {}
}
#[derive(PartialEq, Eq)]
@ -138,8 +147,7 @@ enum WhoKnows<T> {
match WHOKNOWSQUUX {
WHOKNOWSQUUX => {}
WhoKnows::Yay(_) => {}
WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer
//~^ ERROR unreachable pattern
WHOKNOWSQUUX => {}
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)]`
--> $DIR/consts-opaque.rs:30:9
--> $DIR/consts-opaque.rs:31:9
|
LL | FOO => {}
| ^^^
@ -8,7 +8,7 @@ LL | FOO => {}
= 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)]`
--> $DIR/consts-opaque.rs:37:9
--> $DIR/consts-opaque.rs:38:9
|
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
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 => {}
| ^^^^^^^^^^^
@ -29,7 +29,7 @@ LL | FOO_REF_REF => {}
= 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)]`
--> $DIR/consts-opaque.rs:53:9
--> $DIR/consts-opaque.rs:54:9
|
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
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 => {}
| ^^^
@ -47,7 +47,7 @@ LL | BAR => {}
= 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)]`
--> $DIR/consts-opaque.rs:70:9
--> $DIR/consts-opaque.rs:71:9
|
LL | BAR => {}
| ^^^
@ -56,7 +56,7 @@ LL | BAR => {}
= 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)]`
--> $DIR/consts-opaque.rs:72:9
--> $DIR/consts-opaque.rs:73:9
|
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
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 => {}
| ^^^
@ -74,7 +74,7 @@ LL | BAZ => {}
= 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)]`
--> $DIR/consts-opaque.rs:90:9
--> $DIR/consts-opaque.rs:91:9
|
LL | BAZ => {}
| ^^^
@ -83,7 +83,7 @@ LL | BAZ => {}
= 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)]`
--> $DIR/consts-opaque.rs:97:9
--> $DIR/consts-opaque.rs:98:9
|
LL | BAZ => {}
| ^^^
@ -92,7 +92,7 @@ LL | BAZ => {}
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
error: unreachable pattern
--> $DIR/consts-opaque.rs:32:9
--> $DIR/consts-opaque.rs:33:9
|
LL | FOO => {}
| --- matches any value
@ -107,7 +107,7 @@ LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/consts-opaque.rs:39:9
--> $DIR/consts-opaque.rs:40:9
|
LL | FOO_REF => {}
| ------- matches any value
@ -116,7 +116,7 @@ LL | Foo(_) => {} // should not be emitting unreachable warning
| ^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:53:9
--> $DIR/consts-opaque.rs:54:9
|
LL | Bar => {}
| --- matches any value
@ -124,7 +124,7 @@ LL | BAR => {} // should not be emitting unreachable warning
| ^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:56:9
--> $DIR/consts-opaque.rs:57:9
|
LL | Bar => {}
| --- matches any value
@ -133,7 +133,7 @@ LL | _ => {}
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:63:9
--> $DIR/consts-opaque.rs:64:9
|
LL | BAR => {}
| --- matches any value
@ -142,7 +142,7 @@ LL | Bar => {} // should not be emitting unreachable warning
| ^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:65:9
--> $DIR/consts-opaque.rs:66:9
|
LL | BAR => {}
| --- matches any value
@ -151,7 +151,7 @@ LL | _ => {}
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:72:9
--> $DIR/consts-opaque.rs:73:9
|
LL | BAR => {}
| --- matches any value
@ -160,7 +160,7 @@ LL | BAR => {} // should not be emitting unreachable warning
| ^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:75:9
--> $DIR/consts-opaque.rs:76:9
|
LL | BAR => {}
| --- matches any value
@ -169,7 +169,7 @@ LL | _ => {} // should not be emitting unreachable warning
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:82:9
--> $DIR/consts-opaque.rs:83:9
|
LL | BAZ => {}
| --- matches any value
@ -178,7 +178,7 @@ LL | Baz::Baz1 => {} // should not be emitting unreachable warning
| ^^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:84:9
--> $DIR/consts-opaque.rs:85:9
|
LL | BAZ => {}
| --- matches any value
@ -187,7 +187,7 @@ LL | _ => {}
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:92:9
--> $DIR/consts-opaque.rs:93:9
|
LL | BAZ => {}
| --- matches any value
@ -196,7 +196,7 @@ LL | _ => {}
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:99:9
--> $DIR/consts-opaque.rs:100:9
|
LL | BAZ => {}
| --- matches any value
@ -205,7 +205,7 @@ LL | Baz::Baz2 => {} // should not be emitting unreachable warning
| ^^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:101:9
--> $DIR/consts-opaque.rs:102:9
|
LL | BAZ => {}
| --- matches any value
@ -213,19 +213,24 @@ LL | BAZ => {}
LL | _ => {} // should not be emitting unreachable warning
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:127:9
error[E0004]: non-exhaustive patterns: `Wrap(_)` not covered
--> $DIR/consts-opaque.rs:135:11
|
LL | Wrap(_) => {}
| ------- matches any value
LL | WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
| ^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:141:9
LL | match WRAPQUUX {
| ^^^^^^^^ pattern `Wrap(_)` not covered
|
note: `Wrap<fn(usize, usize) -> usize>` defined here
--> $DIR/consts-opaque.rs:117:12
|
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`.