Skip to content

Commit 3c23df4

Browse files
committed
Auto merge of #115937 - oli-obk:spurious_unreachable_pattern, r=Nadrieril
Prevent spurious `unreachable pattern` lints But it means we'll get more `non-exhaustive` patterns fixes #78057 r? `@Nadrieril` `@RalfJung`
2 parents 5aa23be + eca786c commit 3c23df4

File tree

11 files changed

+115
-203
lines changed

11 files changed

+115
-203
lines changed

compiler/rustc_middle/src/thir.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,13 @@ pub enum BindingMode {
581581
ByRef(BorrowKind),
582582
}
583583

584-
#[derive(Clone, Debug, HashStable)]
584+
#[derive(Clone, Debug, HashStable, TypeVisitable)]
585585
pub struct FieldPat<'tcx> {
586586
pub field: FieldIdx,
587587
pub pattern: Box<Pat<'tcx>>,
588588
}
589589

590-
#[derive(Clone, Debug, HashStable)]
590+
#[derive(Clone, Debug, HashStable, TypeVisitable)]
591591
pub struct Pat<'tcx> {
592592
pub ty: Ty<'tcx>,
593593
pub span: Span,
@@ -664,7 +664,7 @@ impl<'tcx> IntoDiagnosticArg for Pat<'tcx> {
664664
}
665665
}
666666

667-
#[derive(Clone, Debug, HashStable)]
667+
#[derive(Clone, Debug, HashStable, TypeVisitable)]
668668
pub struct Ascription<'tcx> {
669669
pub annotation: CanonicalUserTypeAnnotation<'tcx>,
670670
/// Variance to use when relating the `user_ty` to the **type of the value being
@@ -688,7 +688,7 @@ pub struct Ascription<'tcx> {
688688
pub variance: ty::Variance,
689689
}
690690

691-
#[derive(Clone, Debug, HashStable)]
691+
#[derive(Clone, Debug, HashStable, TypeVisitable)]
692692
pub enum PatKind<'tcx> {
693693
/// A wildcard pattern: `_`.
694694
Wild,
@@ -702,7 +702,9 @@ pub enum PatKind<'tcx> {
702702
Binding {
703703
mutability: Mutability,
704704
name: Symbol,
705+
#[type_visitable(ignore)]
705706
mode: BindingMode,
707+
#[type_visitable(ignore)]
706708
var: LocalVarId,
707709
ty: Ty<'tcx>,
708710
subpattern: Option<Box<Pat<'tcx>>>,
@@ -771,10 +773,11 @@ pub enum PatKind<'tcx> {
771773
},
772774
}
773775

774-
#[derive(Clone, Debug, PartialEq, HashStable)]
776+
#[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)]
775777
pub struct PatRange<'tcx> {
776778
pub lo: mir::Const<'tcx>,
777779
pub hi: mir::Const<'tcx>,
780+
#[type_visitable(ignore)]
778781
pub end: RangeEnd,
779782
}
780783

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_hir::HirId;
1919
use rustc_middle::thir::visit::{self, Visitor};
2020
use rustc_middle::thir::*;
2121
use rustc_middle::ty::print::with_no_trimmed_paths;
22-
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
22+
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt};
2323
use rustc_session::lint::builtin::{
2424
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
2525
};
@@ -682,6 +682,12 @@ fn non_exhaustive_match<'p, 'tcx>(
682682
arms: &[ArmId],
683683
expr_span: Span,
684684
) -> ErrorGuaranteed {
685+
for &arm in arms {
686+
if let Err(err) = thir[arm].pattern.error_reported() {
687+
return err;
688+
}
689+
}
690+
685691
let is_empty_match = arms.is_empty();
686692
let non_empty_enum = match scrut_ty.kind() {
687693
ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),

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

+48-37
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_middle::mir;
77
use rustc_middle::thir::{FieldPat, Pat, PatKind};
88
use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
99
use rustc_session::lint;
10-
use rustc_span::Span;
10+
use rustc_span::{ErrorGuaranteed, Span};
1111
use rustc_target::abi::{FieldIdx, VariantIdx};
1212
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1313
use rustc_trait_selection::traits::{self, ObligationCause};
@@ -48,7 +48,7 @@ struct ConstToPat<'tcx> {
4848
// This tracks if we emitted some hard error for a given const value, so that
4949
// we will not subsequently issue an irrelevant lint for the same const
5050
// value.
51-
saw_const_match_error: Cell<bool>,
51+
saw_const_match_error: Cell<Option<ErrorGuaranteed>>,
5252

5353
// This tracks if we emitted some diagnostic for a given const value, so that
5454
// we will not subsequently issue an irrelevant lint for the same const
@@ -84,7 +84,7 @@ impl<'tcx> ConstToPat<'tcx> {
8484
span,
8585
infcx,
8686
param_env: pat_ctxt.param_env,
87-
saw_const_match_error: Cell::new(false),
87+
saw_const_match_error: Cell::new(None),
8888
saw_const_match_lint: Cell::new(false),
8989
behind_reference: Cell::new(false),
9090
treat_byte_string_as_slice: pat_ctxt
@@ -154,7 +154,7 @@ impl<'tcx> ConstToPat<'tcx> {
154154
}),
155155
};
156156

157-
if !self.saw_const_match_error.get() {
157+
if self.saw_const_match_error.get().is_none() {
158158
// If we were able to successfully convert the const to some pat (possibly with some
159159
// lints, but no errors), double-check that all types in the const implement
160160
// `Structural` and `PartialEq`.
@@ -180,23 +180,26 @@ impl<'tcx> ConstToPat<'tcx> {
180180

181181
if let Some(non_sm_ty) = structural {
182182
if !self.type_has_partial_eq_impl(cv.ty()) {
183-
if let ty::Adt(def, ..) = non_sm_ty.kind() {
183+
let e = if let ty::Adt(def, ..) = non_sm_ty.kind() {
184184
if def.is_union() {
185185
let err = UnionPattern { span: self.span };
186-
self.tcx().sess.emit_err(err);
186+
self.tcx().sess.emit_err(err)
187187
} else {
188188
// fatal avoids ICE from resolution of nonexistent method (rare case).
189189
self.tcx()
190190
.sess
191-
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty });
191+
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty })
192192
}
193193
} else {
194194
let err = InvalidPattern { span: self.span, non_sm_ty };
195-
self.tcx().sess.emit_err(err);
196-
}
195+
self.tcx().sess.emit_err(err)
196+
};
197197
// 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 });
198+
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
199+
let kind = PatKind::Constant {
200+
value: mir::Const::Ty(ty::Const::new_error(self.tcx(), e, cv.ty())),
201+
};
202+
return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
200203
} else if !self.saw_const_match_lint.get() {
201204
if let Some(mir_structural_match_violation) = mir_structural_match_violation {
202205
match non_sm_ty.kind() {
@@ -330,7 +333,7 @@ impl<'tcx> ConstToPat<'tcx> {
330333
// Backwards compatibility hack because we can't cause hard errors on these
331334
// types, so we compare them via `PartialEq::eq` at runtime.
332335
ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => {
333-
if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
336+
if self.saw_const_match_error.get().is_none() && !self.saw_const_match_lint.get() {
334337
self.saw_const_match_lint.set(true);
335338
tcx.emit_spanned_lint(
336339
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
@@ -345,18 +348,18 @@ impl<'tcx> ConstToPat<'tcx> {
345348
return Err(FallbackToOpaqueConst);
346349
}
347350
ty::FnDef(..) => {
348-
self.saw_const_match_error.set(true);
349-
tcx.sess.emit_err(InvalidPattern { span, non_sm_ty: ty });
350-
// We errored, so the pattern we generate is irrelevant.
351-
PatKind::Wild
351+
let e = tcx.sess.emit_err(InvalidPattern { span, non_sm_ty: ty });
352+
self.saw_const_match_error.set(Some(e));
353+
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
354+
PatKind::Constant { value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)) }
352355
}
353356
ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
354357
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,);
355-
self.saw_const_match_error.set(true);
356358
let err = TypeNotStructural { span, non_sm_ty: ty };
357-
tcx.sess.emit_err(err);
358-
// We errored, so the pattern we generate is irrelevant.
359-
PatKind::Wild
359+
let e = tcx.sess.emit_err(err);
360+
self.saw_const_match_error.set(Some(e));
361+
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
362+
PatKind::Constant { value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)) }
360363
}
361364
ty::Adt(adt_def, args) if adt_def.is_enum() => {
362365
let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
@@ -416,7 +419,9 @@ impl<'tcx> ConstToPat<'tcx> {
416419
// instead of a hard error.
417420
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
418421
if self.behind_reference.get() {
419-
if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
422+
if self.saw_const_match_error.get().is_none()
423+
&& !self.saw_const_match_lint.get()
424+
{
420425
self.saw_const_match_lint.set(true);
421426
tcx.emit_spanned_lint(
422427
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
@@ -427,14 +432,20 @@ impl<'tcx> ConstToPat<'tcx> {
427432
}
428433
return Err(FallbackToOpaqueConst);
429434
} else {
430-
if !self.saw_const_match_error.get() {
431-
self.saw_const_match_error.set(true);
435+
if let Some(e) = self.saw_const_match_error.get() {
436+
// We already errored. Signal that in the pattern, so that follow up errors can be silenced.
437+
PatKind::Constant {
438+
value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)),
439+
}
440+
} else {
432441
let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
433-
tcx.sess.emit_err(err);
442+
let e = tcx.sess.emit_err(err);
443+
self.saw_const_match_error.set(Some(e));
444+
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
445+
PatKind::Constant {
446+
value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)),
447+
}
434448
}
435-
tcx.sess.delay_span_bug(span, "`saw_const_match_error` set but no error?");
436-
// We errored, so the pattern we generate is irrelevant.
437-
PatKind::Wild
438449
}
439450
}
440451
// All other references are converted into deref patterns and then recursively
@@ -443,11 +454,11 @@ impl<'tcx> ConstToPat<'tcx> {
443454
_ => {
444455
if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
445456
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
446-
tcx.sess.emit_err(err);
447-
448-
// FIXME: introduce PatKind::Error to silence follow up diagnostics due to unreachable patterns.
449-
// We errored, so the pattern we generate is irrelevant.
450-
PatKind::Wild
457+
let e = tcx.sess.emit_err(err);
458+
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
459+
PatKind::Constant {
460+
value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)),
461+
}
451462
} else {
452463
let old = self.behind_reference.replace(true);
453464
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
@@ -474,15 +485,15 @@ impl<'tcx> ConstToPat<'tcx> {
474485
}
475486
ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(),
476487
_ => {
477-
self.saw_const_match_error.set(true);
478488
let err = InvalidPattern { span, non_sm_ty: ty };
479-
tcx.sess.emit_err(err);
480-
// We errored, so the pattern we generate is irrelevant.
481-
PatKind::Wild
489+
let e = tcx.sess.emit_err(err);
490+
self.saw_const_match_error.set(Some(e));
491+
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
492+
PatKind::Constant { value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)) }
482493
}
483494
};
484495

485-
if !self.saw_const_match_error.get()
496+
if self.saw_const_match_error.get().is_none()
486497
&& !self.saw_const_match_lint.get()
487498
&& mir_structural_match_violation
488499
// FIXME(#73448): Find a way to bring const qualification into parity with

compiler/rustc_type_ir/src/structural_impls.rs

+6
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
153153
}
154154
}
155155

156+
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
157+
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
158+
self.iter().try_for_each(|t| t.visit_with(visitor))
159+
}
160+
}
161+
156162
impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> {
157163
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
158164
self.try_map_id(|x| x.try_fold_with(folder))

tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,17 @@ LL | WHAT_A_TYPE => 0,
77
= note: the traits must be derived, manual `impl`s are not sufficient
88
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
99

10-
error: aborting due to previous error
10+
error[E0015]: cannot match on `TypeId` in constant functions
11+
--> $DIR/typeid-equality-by-subtyping.rs:18:9
12+
|
13+
LL | WHAT_A_TYPE => 0,
14+
| ^^^^^^^^^^^
15+
|
16+
= note: `TypeId` cannot be compared in compile-time, and therefore cannot be used in `match`es
17+
note: impl defined here, but it is not `const`
18+
--> $SRC_DIR/core/src/any.rs:LL:COL
19+
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
20+
21+
error: aborting due to 2 previous errors
1122

23+
For more information about this error, try `rustc --explain E0015`.

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

-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,5 @@ fn main() {
1212
FOO => {},
1313
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
1414
_ => {}
15-
//~^ ERROR unreachable pattern
1615
}
1716
}

tests/ui/consts/const_in_pattern/issue-78057.stderr

+1-16
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,5 @@ LL | FOO => {},
77
= note: the traits must be derived, manual `impl`s are not sufficient
88
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
99

10-
error: unreachable pattern
11-
--> $DIR/issue-78057.rs:14:9
12-
|
13-
LL | FOO => {},
14-
| --- matches any value
15-
LL |
16-
LL | _ => {}
17-
| ^ unreachable pattern
18-
|
19-
note: the lint level is defined here
20-
--> $DIR/issue-78057.rs:1:9
21-
|
22-
LL | #![deny(unreachable_patterns)]
23-
| ^^^^^^^^^^^^^^^^^^^^
24-
25-
error: aborting due to 2 previous errors
10+
error: aborting due to previous error
2611

tests/ui/pattern/non-structural-match-types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
fn main() {
88
match loop {} {
9-
const { || {} } => {}, //~ ERROR cannot be used in patterns
9+
const { || {} } => {} //~ ERROR cannot be used in patterns
1010
}
1111
match loop {} {
12-
const { async {} } => {}, //~ ERROR cannot be used in patterns
12+
const { async {} } => {} //~ ERROR cannot be used in patterns
1313
}
1414
}

tests/ui/pattern/non-structural-match-types.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
error: `{closure@$DIR/non-structural-match-types.rs:9:17: 9:19}` cannot be used in patterns
22
--> $DIR/non-structural-match-types.rs:9:9
33
|
4-
LL | const { || {} } => {},
4+
LL | const { || {} } => {}
55
| ^^^^^^^^^^^^^^^
66

77
error: `{async block@$DIR/non-structural-match-types.rs:12:17: 12:25}` cannot be used in patterns
88
--> $DIR/non-structural-match-types.rs:12:9
99
|
10-
LL | const { async {} } => {},
10+
LL | const { async {} } => {}
1111
| ^^^^^^^^^^^^^^^^^^
1212

1313
error: aborting due to 2 previous errors

0 commit comments

Comments
 (0)