Check bindings around never patterns

This commit is contained in:
Nadrieril 2024-01-05 10:11:18 +01:00
parent b31735a401
commit 560beb1ad4
13 changed files with 110 additions and 105 deletions

View file

@ -960,6 +960,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
.create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }),
ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }),
ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }),
ResolutionError::BindingInNeverPattern => {
self.dcx().create_err(errs::BindingInNeverPattern { span })
}
}
}

View file

@ -486,6 +486,15 @@ pub(crate) struct LowercaseSelf {
pub(crate) span: Span,
}
#[derive(Debug)]
#[derive(Diagnostic)]
#[diag(resolve_binding_in_never_pattern)]
pub(crate) struct BindingInNeverPattern {
#[primary_span]
#[suggestion(code = "_", applicability = "machine-applicable", style = "short")]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(resolve_trait_impl_duplicate, code = "E0201")]
pub(crate) struct TraitImplDuplicate {

View file

@ -65,6 +65,8 @@ enum IsRepeatExpr {
Yes,
}
struct IsNeverPattern;
/// Describes whether an `AnonConst` is a type level const arg or
/// some other form of anon const (i.e. inline consts or enum discriminants)
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -3191,11 +3193,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
/// Build a map from pattern identifiers to binding-info's, and check the bindings are
/// consistent when encountering or-patterns.
/// consistent when encountering or-patterns and never patterns.
/// This is done hygienically: this could arise for a macro that expands into an or-pattern
/// where one 'x' was from the user and one 'x' came from the macro.
fn compute_and_check_binding_map(&mut self, pat: &Pat) -> FxIndexMap<Ident, BindingInfo> {
fn compute_and_check_binding_map(
&mut self,
pat: &Pat,
) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> {
let mut binding_map = FxIndexMap::default();
let mut is_never_pat = false;
pat.walk(&mut |pat| {
match pat.kind {
@ -3207,17 +3213,26 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
PatKind::Or(ref ps) => {
// Check the consistency of this or-pattern and
// then add all bindings to the larger map.
let bm = self.compute_and_check_or_pat_binding_map(ps);
let (bm, np) = self.compute_and_check_or_pat_binding_map(ps);
binding_map.extend(bm);
is_never_pat |= np;
return false;
}
PatKind::Never => is_never_pat = true,
_ => {}
}
true
});
binding_map
if is_never_pat {
for (_, binding) in binding_map {
self.report_error(binding.span, ResolutionError::BindingInNeverPattern);
}
Err(IsNeverPattern)
} else {
Ok(binding_map)
}
}
fn is_base_res_local(&self, nid: NodeId) -> bool {
@ -3229,24 +3244,29 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
/// Compute the binding map for an or-pattern. Checks that all of the arms in the or-pattern
/// have exactly the same set of bindings, with the same binding modes for each.
/// Returns the computed binding map.
/// Returns the computed binding map and a boolean indicating whether the pattern is a never
/// pattern.
fn compute_and_check_or_pat_binding_map(
&mut self,
pats: &[P<Pat>],
) -> FxIndexMap<Ident, BindingInfo> {
) -> (FxIndexMap<Ident, BindingInfo>, bool) {
let mut missing_vars = FxIndexMap::default();
let mut inconsistent_vars = FxIndexMap::default();
// 1) Compute the binding maps of all arms.
let maps =
pats.iter().map(|pat| self.compute_and_check_binding_map(pat)).collect::<Vec<_>>();
// 1) Compute the binding maps of all arms; never patterns don't participate in this.
let not_never_pats = pats
.iter()
.filter_map(|pat| {
let binding_map = self.compute_and_check_binding_map(pat).ok()?;
Some((binding_map, pat))
})
.collect::<Vec<_>>();
// 2) Record any missing bindings or binding mode inconsistencies.
for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) {
for (map_outer, pat_outer) in not_never_pats.iter() {
// Check against all arms except for the same pattern which is always self-consistent.
let inners = maps
let inners = not_never_pats
.iter()
.zip(pats.iter())
.filter(|(_, pat)| pat.id != pat_outer.id)
.flat_map(|(map, _)| map);
@ -3294,22 +3314,17 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
// 5) Bubble up the final binding map.
let is_never_pat = not_never_pats.is_empty();
let mut binding_map = FxIndexMap::default();
for bm in maps {
for (bm, _) in not_never_pats {
binding_map.extend(bm);
}
binding_map
(binding_map, is_never_pat)
}
/// Check the consistency of bindings wrt or-patterns.
/// Check the consistency of bindings wrt or-patterns and never patterns.
fn check_consistent_bindings(&mut self, pat: &'ast Pat) {
pat.walk(&mut |pat| match pat.kind {
PatKind::Or(ref ps) => {
let _ = self.compute_and_check_or_pat_binding_map(ps);
false
}
_ => true,
})
let _ = self.compute_and_check_binding_map(pat);
}
fn resolve_arm(&mut self, arm: &'ast Arm) {

View file

@ -265,6 +265,8 @@ enum ResolutionError<'a> {
InvalidAsmSym,
/// `self` used instead of `Self` in a generic parameter
LowercaseSelf,
/// A never pattern has a binding.
BindingInNeverPattern,
}
enum VisResolutionError<'a> {