Skip to content

Commit 1997c70

Browse files
committed
Track place validity
1 parent ca08edb commit 1997c70

File tree

2 files changed

+204
-29
lines changed

2 files changed

+204
-29
lines changed

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

+110-14
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
4242

4343
for param in thir.params.iter() {
4444
if let Some(box ref pattern) = param.pat {
45-
visitor.check_binding_is_irrefutable(pattern, "function argument", None);
45+
visitor.check_binding_is_irrefutable(pattern, "function argument", None, None);
4646
}
4747
}
4848
visitor.error
@@ -254,10 +254,11 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
254254
self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
255255
}
256256
ExprKind::Let { box ref pat, expr } => {
257+
let expr = &self.thir()[expr];
257258
self.with_let_source(LetSource::None, |this| {
258-
this.visit_expr(&this.thir()[expr]);
259+
this.visit_expr(expr);
259260
});
260-
Ok(Some((ex.span, self.is_let_irrefutable(pat)?)))
261+
Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
261262
}
262263
_ => {
263264
self.with_let_source(LetSource::None, |this| {
@@ -287,35 +288,114 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
287288
}
288289
}
289290

291+
/// Inspects the match scrutinee expression to determine whether the place it evaluates to may
292+
/// hold invalid data.
293+
fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
294+
use ExprKind::*;
295+
match &scrutinee.kind {
296+
// Both pointers and references can validly point to a place with invalid data.
297+
Deref { .. } => false,
298+
// Inherit validity of the parent place, unless the parent is an union.
299+
Field { lhs, .. } => {
300+
let lhs = &self.thir()[*lhs];
301+
match lhs.ty.kind() {
302+
ty::Adt(def, _) if def.is_union() => false,
303+
_ => self.is_known_valid_scrutinee(lhs),
304+
}
305+
}
306+
// Essentially a field access.
307+
Index { lhs, .. } => {
308+
let lhs = &self.thir()[*lhs];
309+
self.is_known_valid_scrutinee(lhs)
310+
}
311+
312+
// No-op.
313+
Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
314+
315+
// Casts don't cause a load.
316+
NeverToAny { source }
317+
| Cast { source }
318+
| Use { source }
319+
| PointerCoercion { source, .. }
320+
| PlaceTypeAscription { source, .. }
321+
| ValueTypeAscription { source, .. } => {
322+
self.is_known_valid_scrutinee(&self.thir()[*source])
323+
}
324+
325+
// These diverge.
326+
Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
327+
328+
// These are statements that evaluate to `()`.
329+
Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
330+
331+
// These evaluate to a value.
332+
AddressOf { .. }
333+
| Adt { .. }
334+
| Array { .. }
335+
| Binary { .. }
336+
| Block { .. }
337+
| Borrow { .. }
338+
| Box { .. }
339+
| Call { .. }
340+
| Closure { .. }
341+
| ConstBlock { .. }
342+
| ConstParam { .. }
343+
| If { .. }
344+
| Literal { .. }
345+
| LogicalOp { .. }
346+
| Loop { .. }
347+
| Match { .. }
348+
| NamedConst { .. }
349+
| NonHirLiteral { .. }
350+
| OffsetOf { .. }
351+
| Repeat { .. }
352+
| StaticRef { .. }
353+
| ThreadLocalRef { .. }
354+
| Tuple { .. }
355+
| Unary { .. }
356+
| UpvarRef { .. }
357+
| VarRef { .. }
358+
| ZstLiteral { .. }
359+
| Yield { .. } => true,
360+
}
361+
}
362+
290363
fn new_cx(
291364
&self,
292365
refutability: RefutableFlag,
293-
match_span: Option<Span>,
366+
whole_match_span: Option<Span>,
367+
scrutinee: Option<&Expr<'tcx>>,
294368
scrut_span: Span,
295369
) -> MatchCheckCtxt<'p, 'tcx> {
296370
let refutable = match refutability {
297371
Irrefutable => false,
298372
Refutable => true,
299373
};
374+
// If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
375+
// require validity.
376+
let known_valid_scrutinee =
377+
scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
300378
MatchCheckCtxt {
301379
tcx: self.tcx,
302380
param_env: self.param_env,
303381
module: self.tcx.parent_module(self.lint_level).to_def_id(),
304382
pattern_arena: self.pattern_arena,
305383
match_lint_level: self.lint_level,
306-
match_span,
384+
whole_match_span,
307385
scrut_span,
308386
refutable,
387+
known_valid_scrutinee,
309388
}
310389
}
311390

312391
#[instrument(level = "trace", skip(self))]
313392
fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
314393
assert!(self.let_source != LetSource::None);
394+
let scrut = scrutinee.map(|id| &self.thir[id]);
315395
if let LetSource::PlainLet = self.let_source {
316-
self.check_binding_is_irrefutable(pat, "local binding", Some(span))
396+
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
317397
} else {
318-
let Ok(refutability) = self.is_let_irrefutable(pat) else { return };
398+
let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
319399
if matches!(refutability, Irrefutable) {
320400
report_irrefutable_let_patterns(
321401
self.tcx,
@@ -336,7 +416,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
336416
expr_span: Span,
337417
) {
338418
let scrut = &self.thir[scrut];
339-
let cx = self.new_cx(Refutable, Some(expr_span), scrut.span);
419+
let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
340420

341421
let mut tarms = Vec::with_capacity(arms.len());
342422
for &arm in arms {
@@ -377,7 +457,12 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
377457
debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
378458
let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
379459
let [pat_field] = &subpatterns[..] else { bug!() };
380-
self.check_binding_is_irrefutable(&pat_field.pattern, "`for` loop binding", None);
460+
self.check_binding_is_irrefutable(
461+
&pat_field.pattern,
462+
"`for` loop binding",
463+
None,
464+
None,
465+
);
381466
} else {
382467
self.error = Err(report_non_exhaustive_match(
383468
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
@@ -457,16 +542,21 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
457542
&mut self,
458543
pat: &Pat<'tcx>,
459544
refutability: RefutableFlag,
545+
scrut: Option<&Expr<'tcx>>,
460546
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
461-
let cx = self.new_cx(refutability, None, pat.span);
547+
let cx = self.new_cx(refutability, None, scrut, pat.span);
462548
let pat = self.lower_pattern(&cx, pat)?;
463549
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
464550
let report = compute_match_usefulness(&cx, &arms, pat.ty());
465551
Ok((cx, report))
466552
}
467553

468-
fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result<RefutableFlag, ErrorGuaranteed> {
469-
let (cx, report) = self.analyze_binding(pat, Refutable)?;
554+
fn is_let_irrefutable(
555+
&mut self,
556+
pat: &Pat<'tcx>,
557+
scrut: Option<&Expr<'tcx>>,
558+
) -> Result<RefutableFlag, ErrorGuaranteed> {
559+
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
470560
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
471561
// This also reports unreachable sub-patterns.
472562
report_arm_reachability(&cx, &report);
@@ -476,10 +566,16 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
476566
}
477567

478568
#[instrument(level = "trace", skip(self))]
479-
fn check_binding_is_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
569+
fn check_binding_is_irrefutable(
570+
&mut self,
571+
pat: &Pat<'tcx>,
572+
origin: &str,
573+
scrut: Option<&Expr<'tcx>>,
574+
sp: Option<Span>,
575+
) {
480576
let pattern_ty = pat.ty;
481577

482-
let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable) else { return };
578+
let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
483579
let witnesses = report.non_exhaustiveness_witnesses;
484580
if witnesses.is_empty() {
485581
// The pattern is irrefutable.

0 commit comments

Comments
 (0)