Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal: Update match checking algorithm #10484

Merged
merged 4 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/hir_ty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ chalk-ir = "0.75"
chalk-recursive = { version = "0.75", default-features = false }
la-arena = { version = "0.3.0", path = "../../lib/arena" }
once_cell = { version = "1.5.0" }
typed-arena = "2.0.1"

stdx = { path = "../stdx", version = "0.0.0" }
hir_def = { path = "../hir_def", version = "0.0.0" }
Expand Down
50 changes: 22 additions & 28 deletions crates/hir_ty/src/diagnostics/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! through the body using inference results: mismatched arg counts, missing
//! fields, etc.

use std::{cell::RefCell, sync::Arc};
use std::sync::Arc;

use hir_def::{
expr::Statement, path::path, resolver::HasResolver, type_ref::Mutability, AssocItemId,
Expand All @@ -11,12 +11,14 @@ use hir_def::{
use hir_expand::name;
use itertools::Either;
use rustc_hash::FxHashSet;
use typed_arena::Arena;

use crate::{
db::HirDatabase,
diagnostics::match_check::{
self,
usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
deconstruct_pat::DeconstructedPat,
usefulness::{compute_match_usefulness, MatchCheckCtx},
},
AdtId, InferenceResult, Interner, Ty, TyExt, TyKind,
};
Expand Down Expand Up @@ -275,15 +277,20 @@ impl ExprValidator {
) {
let body = db.body(self.owner);

let match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() {
let match_expr_ty = &infer[match_expr];
if match_expr_ty.is_unknown() {
return;
} else {
&infer.type_of_expr[match_expr]
};
}

let pattern_arena = RefCell::new(PatternArena::new());
let pattern_arena = Arena::new();
let cx = MatchCheckCtx {
module: self.owner.module(db.upcast()),
body: self.owner,
db,
pattern_arena: &pattern_arena,
};

let mut m_arms = Vec::new();
let mut m_arms = Vec::with_capacity(arms.len());
let mut has_lowering_errors = false;
for arm in arms {
if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) {
Expand All @@ -308,13 +315,7 @@ impl ExprValidator {
// check the usefulness of each pattern as we added it
// to the matrix here.
let m_arm = match_check::MatchArm {
pat: self.lower_pattern(
arm.pat,
&mut pattern_arena.borrow_mut(),
db,
&body,
&mut has_lowering_errors,
),
pat: self.lower_pattern(&cx, arm.pat, db, &body, &mut has_lowering_errors),
has_guard: arm.guard.is_some(),
};
m_arms.push(m_arm);
Expand All @@ -332,17 +333,10 @@ impl ExprValidator {
return;
}

let cx = MatchCheckCtx {
module: self.owner.module(db.upcast()),
match_expr,
infer: &infer,
db,
pattern_arena: &pattern_arena,
};
let report = compute_match_usefulness(&cx, &m_arms);
let report = compute_match_usefulness(&cx, &m_arms, match_expr_ty);

// FIXME Report unreacheble arms
// https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200

let witnesses = report.non_exhaustiveness_witnesses;
// FIXME Report witnesses
Expand All @@ -352,17 +346,17 @@ impl ExprValidator {
}
}

fn lower_pattern(
fn lower_pattern<'p>(
&self,
cx: &MatchCheckCtx<'_, 'p>,
pat: PatId,
pattern_arena: &mut PatternArena,
db: &dyn HirDatabase,
body: &Body,
have_errors: &mut bool,
) -> match_check::PatId {
) -> &'p DeconstructedPat<'p> {
let mut patcx = match_check::PatCtxt::new(db, &self.infer, body);
let pattern = patcx.lower_pattern(pat);
let pattern = pattern_arena.alloc(expand_pattern(pattern));
let pattern = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern));
if !patcx.errors.is_empty() {
*have_errors = true;
}
Expand Down
46 changes: 24 additions & 22 deletions crates/hir_ty/src/diagnostics/match_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
//!
//! It is modeled on the rustc module `rustc_mir_build::thir::pattern`.

mod deconstruct_pat;
mod pat_util;

pub(crate) mod deconstruct_pat;
pub(crate) mod usefulness;

use hir_def::{body::Body, EnumVariantId, LocalFieldId, VariantId};
use la_arena::Idx;
use hir_def::{body::Body, expr::PatId, EnumVariantId, LocalFieldId, VariantId};
use stdx::never;

use crate::{db::HirDatabase, InferenceResult, Interner, Substitution, Ty, TyKind};
use crate::{
db::HirDatabase, infer::BindingMode, InferenceResult, Interner, Substitution, Ty, TyKind,
};

use self::pat_util::EnumerateAndAdjustIterator;

pub(crate) use self::usefulness::MatchArm;

pub(crate) type PatId = Idx<Pat>;

#[derive(Clone, Debug)]
pub(crate) enum PatternError {
Unimplemented,
UnexpectedType,
UnresolvedVariant,
MissingField,
ExtraFields,
Expand All @@ -41,12 +42,6 @@ pub(crate) struct Pat {
pub(crate) kind: Box<PatKind>,
}

impl Pat {
pub(crate) fn wildcard_from_ty(ty: Ty) -> Self {
Pat { ty, kind: Box::new(PatKind::Wild) }
}
}

/// Close relative to `rustc_mir_build::thir::pattern::PatKind`
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum PatKind {
Expand Down Expand Up @@ -100,7 +95,7 @@ impl<'a> PatCtxt<'a> {
Self { db, infer, body, errors: Vec::new() }
}

pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat {
// XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
// When lowering of & and box patterns are implemented this should be tested
// in a manner of `match_ergonomics_issue_9095` test.
Expand All @@ -116,7 +111,7 @@ impl<'a> PatCtxt<'a> {
)
}

fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat {
let mut ty = &self.infer[pat];
let variant = self.infer.variant_resolution_for_pat(pat);

Expand All @@ -138,9 +133,16 @@ impl<'a> PatCtxt<'a> {
PatKind::Leaf { subpatterns }
}

hir_def::expr::Pat::Bind { subpat, .. } => {
if let TyKind::Ref(.., rty) = ty.kind(Interner) {
ty = rty;
hir_def::expr::Pat::Bind { ref name, subpat, .. } => {
let bm = self.infer.pat_binding_modes[&pat];
match (bm, ty.kind(Interner)) {
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
(BindingMode::Ref(_), _) => {
never!("`ref {}` has wrong type {:?}", name, ty);
self.errors.push(PatternError::UnexpectedType);
return Pat { ty: ty.clone(), kind: PatKind::Wild.into() };
}
_ => (),
}
PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) }
}
Expand Down Expand Up @@ -189,7 +191,7 @@ impl<'a> PatCtxt<'a> {

fn lower_tuple_subpats(
&mut self,
pats: &[hir_def::expr::PatId],
pats: &[PatId],
expected_len: usize,
ellipsis: Option<usize>,
) -> Vec<FieldPat> {
Expand All @@ -207,17 +209,17 @@ impl<'a> PatCtxt<'a> {
.collect()
}

fn lower_patterns(&mut self, pats: &[hir_def::expr::PatId]) -> Vec<Pat> {
fn lower_patterns(&mut self, pats: &[PatId]) -> Vec<Pat> {
pats.iter().map(|&p| self.lower_pattern(p)).collect()
}

fn lower_opt_pattern(&mut self, pat: Option<hir_def::expr::PatId>) -> Option<Pat> {
fn lower_opt_pattern(&mut self, pat: Option<PatId>) -> Option<Pat> {
pat.map(|p| self.lower_pattern(p))
}

fn lower_variant_or_leaf(
&mut self,
pat: hir_def::expr::PatId,
pat: PatId,
ty: &Ty,
subpatterns: Vec<FieldPat>,
) -> PatKind {
Expand All @@ -244,7 +246,7 @@ impl<'a> PatCtxt<'a> {
kind
}

fn lower_path(&mut self, pat: hir_def::expr::PatId, _path: &hir_def::path::Path) -> Pat {
fn lower_path(&mut self, pat: PatId, _path: &hir_def::path::Path) -> Pat {
let ty = &self.infer[pat];

let pat_from_kind = |kind| Pat { ty: ty.clone(), kind: Box::new(kind) };
Expand Down
Loading