-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Always fall back to PartialEq when a constant in a pattern is not recursively structural-eq #105750
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
Changes from all commits
8d00f76
ad424e6
87f9f99
2282258
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 @@ impl<'tcx> ConstToPat<'tcx> { | |
|
||
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 @@ impl<'tcx> ConstToPat<'tcx> { | |
// 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!( | ||
|
@@ -393,11 +385,11 @@ impl<'tcx> ConstToPat<'tcx> { | |
self.behind_reference.set(old); | ||
val | ||
} | ||
// Backwards compatibility hack: support references to non-structural types. | ||
// We'll lower | ||
// this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a | ||
// reference. This makes the rest of the matching logic simpler as it doesn't have | ||
// to figure out how to get a reference again. | ||
// Backwards compatibility hack: support references to non-structural types, | ||
// but hard error if we aren't behind a double reference. We could just use | ||
// the fallback code path below, but that would allow *more* of this fishy | ||
// code to compile, as then it only goes through the future incompat lint | ||
// instead of a hard error. | ||
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { | ||
if self.behind_reference.get() { | ||
if !self.saw_const_match_error.get() | ||
|
@@ -411,7 +403,7 @@ impl<'tcx> ConstToPat<'tcx> { | |
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); | ||
|
@@ -435,24 +427,17 @@ impl<'tcx> ConstToPat<'tcx> { | |
PatKind::Wild | ||
} else { | ||
let old = self.behind_reference.replace(true); | ||
// In case there are structural-match violations somewhere in this subpattern, | ||
// 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 } | ||
|
||
} | ||
} | ||
}, | ||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => { | ||
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 +454,7 @@ impl<'tcx> ConstToPat<'tcx> { | |
PointerPattern | ||
); | ||
} | ||
PatKind::Constant { value: cv } | ||
return Err(FallbackToConstRef); | ||
|
||
} | ||
_ => { | ||
self.saw_const_match_error.set(true); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,12 @@ const BAR: Bar = 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 main() { | |
|
||
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 @@ fn main() { | |
match WHOKNOWSQUUX { | ||
WHOKNOWSQUUX => {} | ||
WhoKnows::Yay(_) => {} | ||
WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer | ||
//~^ ERROR unreachable pattern | ||
WHOKNOWSQUUX => {} | ||
WhoKnows::Nope => {} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.