Skip to content

Commit 1f2bacf

Browse files
committed
Auto merge of rust-lang#115893 - RalfJung:match-require-partial-eq, r=oli-obk
lint towards rejecting consts in patterns that do not implement PartialEq I think we definitely don't want to allow such consts, so even while the general plan around structural matching is up in the air, we can start the process of getting non-PartialEq matches out of the ecosystem.
2 parents 8bf0dec + a1d6fc4 commit 1f2bacf

File tree

8 files changed

+139
-11
lines changed

8 files changed

+139
-11
lines changed

compiler/rustc_lint_defs/src/builtin.rs

+52
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,57 @@ declare_lint! {
23112311
};
23122312
}
23132313

2314+
declare_lint! {
2315+
/// The `const_patterns_without_partial_eq` lint detects constants that are used in patterns,
2316+
/// whose type does not implement `PartialEq`.
2317+
///
2318+
/// ### Example
2319+
///
2320+
/// ```rust,compile_fail
2321+
/// #![deny(const_patterns_without_partial_eq)]
2322+
///
2323+
/// trait EnumSetType {
2324+
/// type Repr;
2325+
/// }
2326+
///
2327+
/// enum Enum8 { }
2328+
/// impl EnumSetType for Enum8 {
2329+
/// type Repr = u8;
2330+
/// }
2331+
///
2332+
/// #[derive(PartialEq, Eq)]
2333+
/// struct EnumSet<T: EnumSetType> {
2334+
/// __enumset_underlying: T::Repr,
2335+
/// }
2336+
///
2337+
/// const CONST_SET: EnumSet<Enum8> = EnumSet { __enumset_underlying: 3 };
2338+
///
2339+
/// fn main() {
2340+
/// match CONST_SET {
2341+
/// CONST_SET => { /* ok */ }
2342+
/// _ => panic!("match fell through?"),
2343+
/// }
2344+
/// }
2345+
/// ```
2346+
///
2347+
/// {{produces}}
2348+
///
2349+
/// ### Explanation
2350+
///
2351+
/// Previous versions of Rust accepted constants in patterns, even if those constants' types
2352+
/// did not have `PartialEq` implemented. The compiler falls back to comparing the value
2353+
/// field-by-field. In the future we'd like to ensure that pattern matching always
2354+
/// follows `PartialEq` semantics, so that trait bound will become a requirement for
2355+
/// matching on constants.
2356+
pub CONST_PATTERNS_WITHOUT_PARTIAL_EQ,
2357+
Warn,
2358+
"constant in pattern does not implement `PartialEq`",
2359+
@future_incompatible = FutureIncompatibleInfo {
2360+
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
2361+
reference: "issue #116122 <https://github.com/rust-lang/rust/issues/116122>",
2362+
};
2363+
}
2364+
23142365
declare_lint! {
23152366
/// The `ambiguous_associated_items` lint detects ambiguity between
23162367
/// [associated items] and [enum variants].
@@ -3357,6 +3408,7 @@ declare_lint_pass! {
33573408
CONFLICTING_REPR_HINTS,
33583409
CONST_EVALUATABLE_UNCHECKED,
33593410
CONST_ITEM_MUTATION,
3411+
CONST_PATTERNS_WITHOUT_PARTIAL_EQ,
33603412
DEAD_CODE,
33613413
DEPRECATED,
33623414
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,

compiler/rustc_mir_build/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type
229229
.suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
230230
.help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
231231
232+
mir_build_non_partial_eq_match =
233+
to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq`
234+
232235
mir_build_nontrivial_structural_match =
233236
to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
234237

compiler/rustc_mir_build/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,12 @@ pub struct NontrivialStructuralMatch<'tcx> {
748748
pub non_sm_ty: Ty<'tcx>,
749749
}
750750

751+
#[derive(LintDiagnostic)]
752+
#[diag(mir_build_non_partial_eq_match)]
753+
pub struct NonPartialEqMatch<'tcx> {
754+
pub non_peq_ty: Ty<'tcx>,
755+
}
756+
751757
#[derive(LintDiagnostic)]
752758
#[diag(mir_build_overlapping_range_endpoints)]
753759
#[note]

compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs

+28-9
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use std::cell::Cell;
1616

1717
use super::PatCtxt;
1818
use crate::errors::{
19-
FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch,
20-
PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
19+
FloatPattern, IndirectStructuralMatch, InvalidPattern, NonPartialEqMatch,
20+
NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
2121
};
2222

2323
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
@@ -155,8 +155,9 @@ impl<'tcx> ConstToPat<'tcx> {
155155
};
156156

157157
if !self.saw_const_match_error.get() {
158-
// If we were able to successfully convert the const to some pat,
159-
// double-check that all types in the const implement `Structural`.
158+
// If we were able to successfully convert the const to some pat (possibly with some
159+
// lints, but no errors), double-check that all types in the const implement
160+
// `Structural` and `PartialEq`.
160161

161162
let structural =
162163
traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
@@ -178,7 +179,7 @@ impl<'tcx> ConstToPat<'tcx> {
178179
}
179180

180181
if let Some(non_sm_ty) = structural {
181-
if !self.type_may_have_partial_eq_impl(cv.ty()) {
182+
if !self.type_has_partial_eq_impl(cv.ty()) {
182183
if let ty::Adt(def, ..) = non_sm_ty.kind() {
183184
if def.is_union() {
184185
let err = UnionPattern { span: self.span };
@@ -192,8 +193,10 @@ impl<'tcx> ConstToPat<'tcx> {
192193
} else {
193194
let err = InvalidPattern { span: self.span, non_sm_ty };
194195
self.tcx().sess.emit_err(err);
195-
return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild });
196196
}
197+
// All branches above emitted an error. Don't print any more lints.
198+
// The pattern we return is irrelevant since we errored.
199+
return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild });
197200
} else if !self.saw_const_match_lint.get() {
198201
if let Some(mir_structural_match_violation) = mir_structural_match_violation {
199202
match non_sm_ty.kind() {
@@ -238,13 +241,24 @@ impl<'tcx> ConstToPat<'tcx> {
238241
_ => {}
239242
}
240243
}
244+
245+
// Always check for `PartialEq`, even if we emitted other lints. (But not if there were
246+
// any errors.) This ensures it shows up in cargo's future-compat reports as well.
247+
if !self.type_has_partial_eq_impl(cv.ty()) {
248+
self.tcx().emit_spanned_lint(
249+
lint::builtin::CONST_PATTERNS_WITHOUT_PARTIAL_EQ,
250+
self.id,
251+
self.span,
252+
NonPartialEqMatch { non_peq_ty: cv.ty() },
253+
);
254+
}
241255
}
242256

243257
inlined_const_as_pat
244258
}
245259

246260
#[instrument(level = "trace", skip(self), ret)]
247-
fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
261+
fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
248262
// double-check there even *is* a semantic `PartialEq` to dispatch to.
249263
//
250264
// (If there isn't, then we can safely issue a hard
@@ -259,8 +273,13 @@ impl<'tcx> ConstToPat<'tcx> {
259273
ty::TraitRef::new(self.tcx(), partial_eq_trait_id, [ty, ty]),
260274
);
261275

262-
// FIXME: should this call a `predicate_must_hold` variant instead?
263-
self.infcx.predicate_may_hold(&partial_eq_obligation)
276+
// This *could* accept a type that isn't actually `PartialEq`, because region bounds get
277+
// ignored. However that should be pretty much impossible since consts that do not depend on
278+
// generics can only mention the `'static` lifetime, and how would one have a type that's
279+
// `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
280+
// we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
281+
// can ensure that the type really implements `PartialEq`.
282+
self.infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation)
264283
}
265284

266285
fn field_pats(

tests/ui/consts/const_in_pattern/issue-65466.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const C: &[O<B>] = &[O::None];
1515
fn main() {
1616
let x = O::None;
1717
match &[x][..] {
18-
C => (),
18+
C => (), //~WARN: the type must implement `PartialEq`
19+
//~| previously accepted
1920
_ => (),
2021
}
2122
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
warning: to use a constant of type `&[O<B>]` in a pattern, the type must implement `PartialEq`
2+
--> $DIR/issue-65466.rs:18:9
3+
|
4+
LL | C => (),
5+
| ^
6+
|
7+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8+
= note: for more information, see issue #116122 <https://github.com/rust-lang/rust/issues/116122>
9+
= note: `#[warn(const_patterns_without_partial_eq)]` on by default
10+
11+
warning: 1 warning emitted
12+
13+
Future incompatibility report: Future breakage diagnostic:
14+
warning: to use a constant of type `&[O<B>]` in a pattern, the type must implement `PartialEq`
15+
--> $DIR/issue-65466.rs:18:9
16+
|
17+
LL | C => (),
18+
| ^
19+
|
20+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
21+
= note: for more information, see issue #116122 <https://github.com/rust-lang/rust/issues/116122>
22+
= note: `#[warn(const_patterns_without_partial_eq)]` on by default
23+

tests/ui/match/issue-72896.rs tests/ui/match/issue-72896-non-partial-eq-const.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const CONST_SET: EnumSet<Enum8> = EnumSet { __enumset_underlying: 3 };
1717

1818
fn main() {
1919
match CONST_SET {
20-
CONST_SET => { /* ok */ }
20+
CONST_SET => { /* ok */ } //~WARN: must implement `PartialEq`
21+
//~| previously accepted
2122
_ => panic!("match fell through?"),
2223
}
2324
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
warning: to use a constant of type `EnumSet<Enum8>` in a pattern, the type must implement `PartialEq`
2+
--> $DIR/issue-72896-non-partial-eq-const.rs:20:9
3+
|
4+
LL | CONST_SET => { /* ok */ }
5+
| ^^^^^^^^^
6+
|
7+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8+
= note: for more information, see issue #116122 <https://github.com/rust-lang/rust/issues/116122>
9+
= note: `#[warn(const_patterns_without_partial_eq)]` on by default
10+
11+
warning: 1 warning emitted
12+
13+
Future incompatibility report: Future breakage diagnostic:
14+
warning: to use a constant of type `EnumSet<Enum8>` in a pattern, the type must implement `PartialEq`
15+
--> $DIR/issue-72896-non-partial-eq-const.rs:20:9
16+
|
17+
LL | CONST_SET => { /* ok */ }
18+
| ^^^^^^^^^
19+
|
20+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
21+
= note: for more information, see issue #116122 <https://github.com/rust-lang/rust/issues/116122>
22+
= note: `#[warn(const_patterns_without_partial_eq)]` on by default
23+

0 commit comments

Comments
 (0)