Rollup merge of #108705 - clubby789:refutable-let-closure-borrow, r=cjgillot
Prevent ICE with broken borrow in closure r? `@Nilstrieb` Fixes #108683 This solution isn't ideal, I'm hoping to find a way to continue compilation without ICEing.
This commit is contained in:
commit
40d933a19a
@ -1016,7 +1016,7 @@
|
|||||||
desc { "converting literal to mir constant" }
|
desc { "converting literal to mir constant" }
|
||||||
}
|
}
|
||||||
|
|
||||||
query check_match(key: LocalDefId) {
|
query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
|
||||||
desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
|
desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
|
||||||
cache_on_disk_if { true }
|
cache_on_disk_if { true }
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,9 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
|
|||||||
// Ensure unsafeck and abstract const building is ran before we steal the THIR.
|
// Ensure unsafeck and abstract const building is ran before we steal the THIR.
|
||||||
tcx.ensure_with_value().thir_check_unsafety(def);
|
tcx.ensure_with_value().thir_check_unsafety(def);
|
||||||
tcx.ensure_with_value().thir_abstract_const(def);
|
tcx.ensure_with_value().thir_abstract_const(def);
|
||||||
tcx.ensure_with_value().check_match(def);
|
if let Err(e) = tcx.check_match(def) {
|
||||||
|
return construct_error(tcx, def, e);
|
||||||
|
}
|
||||||
|
|
||||||
let body = match tcx.thir_body(def) {
|
let body = match tcx.thir_body(def) {
|
||||||
Err(error_reported) => construct_error(tcx, def, error_reported),
|
Err(error_reported) => construct_error(tcx, def, error_reported),
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
use rustc_span::hygiene::DesugaringKind;
|
use rustc_span::hygiene::DesugaringKind;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
|
||||||
let Ok((thir, expr)) = tcx.thir_body(def_id) else { return };
|
let (thir, expr) = tcx.thir_body(def_id)?;
|
||||||
let thir = thir.borrow();
|
let thir = thir.borrow();
|
||||||
let pattern_arena = TypedArena::default();
|
let pattern_arena = TypedArena::default();
|
||||||
let mut visitor = MatchVisitor {
|
let mut visitor = MatchVisitor {
|
||||||
@ -37,13 +37,16 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||||||
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
|
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
|
||||||
let_source: LetSource::None,
|
let_source: LetSource::None,
|
||||||
pattern_arena: &pattern_arena,
|
pattern_arena: &pattern_arena,
|
||||||
|
error: Ok(()),
|
||||||
};
|
};
|
||||||
visitor.visit_expr(&thir[expr]);
|
visitor.visit_expr(&thir[expr]);
|
||||||
|
|
||||||
for param in thir.params.iter() {
|
for param in thir.params.iter() {
|
||||||
if let Some(box ref pattern) = param.pat {
|
if let Some(box ref pattern) = param.pat {
|
||||||
visitor.check_irrefutable(pattern, "function argument", None);
|
visitor.check_irrefutable(pattern, "function argument", None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
visitor.error
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_e0004(
|
fn create_e0004(
|
||||||
@ -77,6 +80,7 @@ struct MatchVisitor<'a, 'p, 'tcx> {
|
|||||||
lint_level: HirId,
|
lint_level: HirId,
|
||||||
let_source: LetSource,
|
let_source: LetSource,
|
||||||
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
|
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
|
||||||
|
error: Result<(), ErrorGuaranteed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
|
impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
|
||||||
@ -276,9 +280,9 @@ fn check_match(
|
|||||||
let [pat_field] = &subpatterns[..] else { bug!() };
|
let [pat_field] = &subpatterns[..] else { bug!() };
|
||||||
self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
|
self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
|
||||||
} else {
|
} else {
|
||||||
non_exhaustive_match(
|
self.error = Err(non_exhaustive_match(
|
||||||
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
|
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,7 +410,7 @@ fn check_let_chain(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self))]
|
#[instrument(level = "trace", skip(self))]
|
||||||
fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
|
fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
|
||||||
let mut cx = self.new_cx(self.lint_level, false);
|
let mut cx = self.new_cx(self.lint_level, false);
|
||||||
|
|
||||||
let pattern = self.lower_pattern(&mut cx, pat);
|
let pattern = self.lower_pattern(&mut cx, pat);
|
||||||
@ -475,7 +479,7 @@ fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
|
|||||||
AdtDefinedHere { adt_def_span, ty, variants }
|
AdtDefinedHere { adt_def_span, ty, variants }
|
||||||
};
|
};
|
||||||
|
|
||||||
self.tcx.sess.emit_err(PatternNotCovered {
|
self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
|
||||||
span: pat.span,
|
span: pat.span,
|
||||||
origin,
|
origin,
|
||||||
uncovered: Uncovered::new(pat.span, &cx, witnesses),
|
uncovered: Uncovered::new(pat.span, &cx, witnesses),
|
||||||
@ -486,7 +490,7 @@ fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
|
|||||||
let_suggestion,
|
let_suggestion,
|
||||||
misc_suggestion,
|
misc_suggestion,
|
||||||
adt_defined_here,
|
adt_defined_here,
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,7 +632,7 @@ fn non_exhaustive_match<'p, 'tcx>(
|
|||||||
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
|
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
|
||||||
arms: &[ArmId],
|
arms: &[ArmId],
|
||||||
expr_span: Span,
|
expr_span: Span,
|
||||||
) {
|
) -> ErrorGuaranteed {
|
||||||
let is_empty_match = arms.is_empty();
|
let is_empty_match = arms.is_empty();
|
||||||
let non_empty_enum = match scrut_ty.kind() {
|
let non_empty_enum = match scrut_ty.kind() {
|
||||||
ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
|
ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
|
||||||
@ -640,13 +644,12 @@ fn non_exhaustive_match<'p, 'tcx>(
|
|||||||
let pattern;
|
let pattern;
|
||||||
let patterns_len;
|
let patterns_len;
|
||||||
if is_empty_match && !non_empty_enum {
|
if is_empty_match && !non_empty_enum {
|
||||||
cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
|
return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
|
||||||
cx,
|
cx,
|
||||||
expr_span,
|
expr_span,
|
||||||
span: sp,
|
span: sp,
|
||||||
ty: scrut_ty,
|
ty: scrut_ty,
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
// FIXME: migration of this diagnostic will require list support
|
// FIXME: migration of this diagnostic will require list support
|
||||||
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
|
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
|
||||||
@ -797,7 +800,7 @@ fn non_exhaustive_match<'p, 'tcx>(
|
|||||||
} else {
|
} else {
|
||||||
err.help(msg);
|
err.help(msg);
|
||||||
}
|
}
|
||||||
err.emit();
|
err.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
|
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
|
||||||
|
23
tests/ui/closures/2229_closure_analysis/bad-pattern.rs
Normal file
23
tests/ui/closures/2229_closure_analysis/bad-pattern.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// regression test for #108683
|
||||||
|
// edition:2021
|
||||||
|
|
||||||
|
enum Refutable {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example(v1: u32, v2: [u32; 4], v3: Refutable) {
|
||||||
|
const PAT: u32 = 0;
|
||||||
|
let v4 = &v2[..];
|
||||||
|
|| {
|
||||||
|
let 0 = v1; //~ ERROR refutable pattern in local binding
|
||||||
|
let (0 | 1) = v1; //~ ERROR refutable pattern in local binding
|
||||||
|
let 1.. = v1; //~ ERROR refutable pattern in local binding
|
||||||
|
let [0, 0, 0, 0] = v2; //~ ERROR refutable pattern in local binding
|
||||||
|
let [0] = v4; //~ ERROR refutable pattern in local binding
|
||||||
|
let Refutable::A = v3; //~ ERROR refutable pattern in local binding
|
||||||
|
let PAT = v1; //~ ERROR refutable pattern in local binding
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
113
tests/ui/closures/2229_closure_analysis/bad-pattern.stderr
Normal file
113
tests/ui/closures/2229_closure_analysis/bad-pattern.stderr
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:13:13
|
||||||
|
|
|
||||||
|
LL | let 0 = v1;
|
||||||
|
| ^ pattern `1_u32..=u32::MAX` not covered
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
= note: the matched value is of type `u32`
|
||||||
|
help: you might want to use `if let` to ignore the variant that isn't matched
|
||||||
|
|
|
||||||
|
LL | if let 0 = v1 { todo!() };
|
||||||
|
| ++ +++++++++++
|
||||||
|
help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
|
||||||
|
|
|
||||||
|
LL | let _0 = v1;
|
||||||
|
| +
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:14:14
|
||||||
|
|
|
||||||
|
LL | let (0 | 1) = v1;
|
||||||
|
| ^^^^^ pattern `2_u32..=u32::MAX` not covered
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
= note: the matched value is of type `u32`
|
||||||
|
help: you might want to use `if let` to ignore the variant that isn't matched
|
||||||
|
|
|
||||||
|
LL | if let (0 | 1) = v1 { todo!() };
|
||||||
|
| ++ +++++++++++
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:15:13
|
||||||
|
|
|
||||||
|
LL | let 1.. = v1;
|
||||||
|
| ^^^ pattern `0_u32` not covered
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
= note: the matched value is of type `u32`
|
||||||
|
help: you might want to use `if let` to ignore the variant that isn't matched
|
||||||
|
|
|
||||||
|
LL | if let 1.. = v1 { todo!() };
|
||||||
|
| ++ +++++++++++
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:16:13
|
||||||
|
|
|
||||||
|
LL | let [0, 0, 0, 0] = v2;
|
||||||
|
| ^^^^^^^^^^^^ pattern `[1_u32..=u32::MAX, _, _, _]` not covered
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
= note: the matched value is of type `[u32; 4]`
|
||||||
|
help: you might want to use `if let` to ignore the variant that isn't matched
|
||||||
|
|
|
||||||
|
LL | if let [0, 0, 0, 0] = v2 { todo!() };
|
||||||
|
| ++ +++++++++++
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:17:13
|
||||||
|
|
|
||||||
|
LL | let [0] = v4;
|
||||||
|
| ^^^ patterns `&[]` and `&[_, _, ..]` not covered
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
= note: the matched value is of type `&[u32]`
|
||||||
|
help: you might want to use `if let` to ignore the variants that aren't matched
|
||||||
|
|
|
||||||
|
LL | if let [0] = v4 { todo!() };
|
||||||
|
| ++ +++++++++++
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:18:13
|
||||||
|
|
|
||||||
|
LL | let Refutable::A = v3;
|
||||||
|
| ^^^^^^^^^^^^ pattern `Refutable::B` not covered
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
note: `Refutable` defined here
|
||||||
|
--> $DIR/bad-pattern.rs:4:6
|
||||||
|
|
|
||||||
|
LL | enum Refutable {
|
||||||
|
| ^^^^^^^^^
|
||||||
|
LL | A,
|
||||||
|
LL | B,
|
||||||
|
| - not covered
|
||||||
|
= note: the matched value is of type `Refutable`
|
||||||
|
help: you might want to use `if let` to ignore the variant that isn't matched
|
||||||
|
|
|
||||||
|
LL | if let Refutable::A = v3 { todo!() };
|
||||||
|
| ++ +++++++++++
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding
|
||||||
|
--> $DIR/bad-pattern.rs:19:13
|
||||||
|
|
|
||||||
|
LL | let PAT = v1;
|
||||||
|
| ^^^
|
||||||
|
| |
|
||||||
|
| pattern `1_u32..=u32::MAX` not covered
|
||||||
|
| missing patterns are not covered because `PAT` is interpreted as a constant pattern, not a new variable
|
||||||
|
| help: introduce a variable instead: `PAT_var`
|
||||||
|
|
|
||||||
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||||
|
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||||
|
= note: the matched value is of type `u32`
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0005`.
|
Loading…
Reference in New Issue
Block a user