Migrate hir-ty body validation diagnostics to the next solver
This commit is contained in:
parent
92eef5348a
commit
5736f47b03
5 changed files with 247 additions and 295 deletions
|
|
@ -1,32 +1,22 @@
|
|||
//! Various extensions traits for Chalk types.
|
||||
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{FunctionId, ItemContainerId, Lookup, TraitId};
|
||||
|
||||
use crate::{
|
||||
AdtId, Binders, CallableDefId, CallableSig, DynTy, Interner, Lifetime, ProjectionTy,
|
||||
Substitution, ToChalk, TraitRef, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase,
|
||||
from_assoc_type_id, from_chalk_trait_id, generics::generics, to_chalk_trait_id,
|
||||
utils::ClosureSubst,
|
||||
Binders, CallableDefId, CallableSig, DynTy, Interner, ProjectionTy, Substitution, ToChalk,
|
||||
TraitRef, Ty, TyKind, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id,
|
||||
generics::generics, to_chalk_trait_id, utils::ClosureSubst,
|
||||
};
|
||||
|
||||
pub(crate) trait TyExt {
|
||||
fn is_unit(&self) -> bool;
|
||||
fn is_unknown(&self) -> bool;
|
||||
fn contains_unknown(&self) -> bool;
|
||||
|
||||
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
|
||||
fn as_tuple(&self) -> Option<&Substitution>;
|
||||
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
|
||||
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
|
||||
|
||||
fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
|
||||
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
|
||||
|
||||
fn strip_references(&self) -> &Ty;
|
||||
|
||||
/// If this is a `dyn Trait`, returns that trait.
|
||||
fn dyn_trait(&self) -> Option<TraitId>;
|
||||
}
|
||||
|
||||
impl TyExt for Ty {
|
||||
|
|
@ -38,17 +28,6 @@ impl TyExt for Ty {
|
|||
matches!(self.kind(Interner), TyKind::Error)
|
||||
}
|
||||
|
||||
fn contains_unknown(&self) -> bool {
|
||||
self.data(Interner).flags.contains(TypeFlags::HAS_ERROR)
|
||||
}
|
||||
|
||||
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_tuple(&self) -> Option<&Substitution> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Tuple(_, substs) => Some(substs),
|
||||
|
|
@ -63,13 +42,6 @@ impl TyExt for Ty {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
|
||||
match self.kind(Interner) {
|
||||
&TyKind::FnDef(def, ..) => Some(ToChalk::from_chalk(db, def)),
|
||||
|
|
@ -85,31 +57,6 @@ impl TyExt for Ty {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn dyn_trait(&self) -> Option<TraitId> {
|
||||
let trait_ref = match self.kind(Interner) {
|
||||
// The principal trait bound should be the first element of the bounds. This is an
|
||||
// invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
|
||||
// FIXME: dyn types may not have principal trait and we don't want to return auto trait
|
||||
// here.
|
||||
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().first().and_then(|b| {
|
||||
match b.skip_binders() {
|
||||
WhereClause::Implemented(trait_ref) => Some(trait_ref),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
}?;
|
||||
Some(from_chalk_trait_id(trait_ref.trait_id))
|
||||
}
|
||||
|
||||
fn strip_references(&self) -> &Ty {
|
||||
let mut t: &Ty = self;
|
||||
while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(Interner) {
|
||||
t = ty;
|
||||
}
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ProjectionTyExt {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use intern::sym;
|
|||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustc_pattern_analysis::constructor::Constructor;
|
||||
use rustc_type_ir::inherent::{AdtDef, IntoKind};
|
||||
use syntax::{
|
||||
AstNode,
|
||||
ast::{self, UnaryOp},
|
||||
|
|
@ -23,16 +24,18 @@ use tracing::debug;
|
|||
use triomphe::Arc;
|
||||
use typed_arena::Arena;
|
||||
|
||||
use crate::next_solver::DbInterner;
|
||||
use crate::next_solver::mapping::NextSolverToChalk;
|
||||
use crate::{
|
||||
Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind,
|
||||
Adjust, InferenceResult, TraitEnvironment,
|
||||
db::HirDatabase,
|
||||
diagnostics::match_check::{
|
||||
self,
|
||||
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
|
||||
},
|
||||
display::{DisplayTarget, HirDisplay},
|
||||
next_solver::{
|
||||
DbInterner, Ty, TyKind, TypingMode,
|
||||
infer::{DbInternerInferExt, InferCtxt},
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) use hir_def::{
|
||||
|
|
@ -77,6 +80,8 @@ impl BodyValidationDiagnostic {
|
|||
let body = db.body(owner);
|
||||
let env = db.trait_environment_for_body(owner);
|
||||
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
|
||||
let infcx =
|
||||
interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into()));
|
||||
let mut validator = ExprValidator {
|
||||
owner,
|
||||
body,
|
||||
|
|
@ -84,9 +89,9 @@ impl BodyValidationDiagnostic {
|
|||
diagnostics: Vec::new(),
|
||||
validate_lints,
|
||||
env,
|
||||
interner,
|
||||
infcx,
|
||||
};
|
||||
validator.validate_body(db);
|
||||
validator.validate_body();
|
||||
validator.diagnostics
|
||||
}
|
||||
}
|
||||
|
|
@ -98,11 +103,17 @@ struct ExprValidator<'db> {
|
|||
env: Arc<TraitEnvironment<'db>>,
|
||||
diagnostics: Vec<BodyValidationDiagnostic>,
|
||||
validate_lints: bool,
|
||||
interner: DbInterner<'db>,
|
||||
infcx: InferCtxt<'db>,
|
||||
}
|
||||
|
||||
impl<'db> ExprValidator<'db> {
|
||||
fn validate_body(&mut self, db: &'db dyn HirDatabase) {
|
||||
#[inline]
|
||||
fn db(&self) -> &'db dyn HirDatabase {
|
||||
self.infcx.interner.db
|
||||
}
|
||||
|
||||
fn validate_body(&mut self) {
|
||||
let db = self.db();
|
||||
let mut filter_map_next_checker = None;
|
||||
// we'll pass &mut self while iterating over body.exprs, so they need to be disjoint
|
||||
let body = Arc::clone(&self.body);
|
||||
|
|
@ -124,19 +135,19 @@ impl<'db> ExprValidator<'db> {
|
|||
|
||||
match expr {
|
||||
Expr::Match { expr, arms } => {
|
||||
self.validate_match(id, *expr, arms, db);
|
||||
self.validate_match(id, *expr, arms);
|
||||
}
|
||||
Expr::Call { .. } | Expr::MethodCall { .. } => {
|
||||
self.validate_call(db, id, expr, &mut filter_map_next_checker);
|
||||
self.validate_call(id, expr, &mut filter_map_next_checker);
|
||||
}
|
||||
Expr::Closure { body: body_expr, .. } => {
|
||||
self.check_for_trailing_return(*body_expr, &body);
|
||||
}
|
||||
Expr::If { .. } => {
|
||||
self.check_for_unnecessary_else(id, expr, db);
|
||||
self.check_for_unnecessary_else(id, expr);
|
||||
}
|
||||
Expr::Block { .. } | Expr::Async { .. } | Expr::Unsafe { .. } => {
|
||||
self.validate_block(db, expr);
|
||||
self.validate_block(expr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -157,10 +168,9 @@ impl<'db> ExprValidator<'db> {
|
|||
|
||||
fn validate_call(
|
||||
&mut self,
|
||||
db: &dyn HirDatabase,
|
||||
call_id: ExprId,
|
||||
expr: &Expr,
|
||||
filter_map_next_checker: &mut Option<FilterMapNextChecker>,
|
||||
filter_map_next_checker: &mut Option<FilterMapNextChecker<'db>>,
|
||||
) {
|
||||
if !self.validate_lints {
|
||||
return;
|
||||
|
|
@ -176,8 +186,9 @@ impl<'db> ExprValidator<'db> {
|
|||
None => return,
|
||||
};
|
||||
|
||||
let checker = filter_map_next_checker
|
||||
.get_or_insert_with(|| FilterMapNextChecker::new(&self.owner.resolver(db), db));
|
||||
let checker = filter_map_next_checker.get_or_insert_with(|| {
|
||||
FilterMapNextChecker::new(&self.owner.resolver(self.db()), self.db())
|
||||
});
|
||||
|
||||
if checker.check(call_id, receiver, &callee).is_some() {
|
||||
self.diagnostics.push(BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
|
||||
|
|
@ -186,27 +197,20 @@ impl<'db> ExprValidator<'db> {
|
|||
}
|
||||
|
||||
if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) {
|
||||
checker.prev_receiver_ty = Some(receiver_ty.to_chalk(self.interner));
|
||||
checker.prev_receiver_ty = Some(receiver_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_match(
|
||||
&mut self,
|
||||
match_expr: ExprId,
|
||||
scrutinee_expr: ExprId,
|
||||
arms: &[MatchArm],
|
||||
db: &dyn HirDatabase,
|
||||
) {
|
||||
fn validate_match(&mut self, match_expr: ExprId, scrutinee_expr: ExprId, arms: &[MatchArm]) {
|
||||
let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else {
|
||||
return;
|
||||
};
|
||||
let scrut_ty = scrut_ty.to_chalk(self.interner);
|
||||
if scrut_ty.contains_unknown() {
|
||||
if scrut_ty.references_non_lt_error() {
|
||||
return;
|
||||
}
|
||||
|
||||
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
|
||||
let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env.clone());
|
||||
|
||||
let pattern_arena = Arena::new();
|
||||
let mut m_arms = Vec::with_capacity(arms.len());
|
||||
|
|
@ -217,8 +221,7 @@ impl<'db> ExprValidator<'db> {
|
|||
let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else {
|
||||
return;
|
||||
};
|
||||
let pat_ty = pat_ty.to_chalk(self.interner);
|
||||
if pat_ty.contains_unknown() {
|
||||
if pat_ty.references_non_lt_error() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -235,14 +238,14 @@ impl<'db> ExprValidator<'db> {
|
|||
if (pat_ty == scrut_ty
|
||||
|| scrut_ty
|
||||
.as_reference()
|
||||
.map(|(match_expr_ty, ..)| *match_expr_ty == pat_ty)
|
||||
.map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
|
||||
.unwrap_or(false))
|
||||
&& types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer)
|
||||
{
|
||||
// If we had a NotUsefulMatchArm diagnostic, we could
|
||||
// check the usefulness of each pattern as we added it
|
||||
// to the matrix here.
|
||||
let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors);
|
||||
let pat = self.lower_pattern(&cx, arm.pat, &mut has_lowering_errors);
|
||||
let m_arm = pat_analysis::MatchArm {
|
||||
pat: pattern_arena.alloc(pat),
|
||||
has_guard: arm.guard.is_some(),
|
||||
|
|
@ -258,15 +261,12 @@ impl<'db> ExprValidator<'db> {
|
|||
return;
|
||||
}
|
||||
|
||||
let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db));
|
||||
let report = match cx.compute_match_usefulness(
|
||||
m_arms.as_slice(),
|
||||
scrut_ty.clone(),
|
||||
known_valid_scrutinee,
|
||||
) {
|
||||
Ok(report) => report,
|
||||
Err(()) => return,
|
||||
};
|
||||
let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr));
|
||||
let report =
|
||||
match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty, known_valid_scrutinee) {
|
||||
Ok(report) => report,
|
||||
Err(()) => return,
|
||||
};
|
||||
|
||||
// FIXME Report unreachable arms
|
||||
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
|
||||
|
|
@ -277,10 +277,10 @@ impl<'db> ExprValidator<'db> {
|
|||
match_expr,
|
||||
uncovered_patterns: missing_match_arms(
|
||||
&cx,
|
||||
&scrut_ty,
|
||||
scrut_ty,
|
||||
witnesses,
|
||||
m_arms.is_empty(),
|
||||
self.owner.krate(db),
|
||||
self.owner.krate(self.db()),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
|
@ -291,7 +291,9 @@ impl<'db> ExprValidator<'db> {
|
|||
// While the above function in rustc uses thir exprs, r-a doesn't have them.
|
||||
// So, the logic here is getting same result as "hir lowering + match with lowered thir"
|
||||
// with "hir only"
|
||||
fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool {
|
||||
fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId) -> bool {
|
||||
let db = self.db();
|
||||
|
||||
if self
|
||||
.infer
|
||||
.expr_adjustments
|
||||
|
|
@ -311,20 +313,18 @@ impl<'db> ExprValidator<'db> {
|
|||
);
|
||||
value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_)))
|
||||
}
|
||||
Expr::Field { expr, .. } => {
|
||||
match self.infer.type_of_expr[*expr].to_chalk(self.interner).kind(Interner) {
|
||||
TyKind::Adt(adt, ..) if matches!(adt.0, AdtId::UnionId(_)) => false,
|
||||
_ => self.is_known_valid_scrutinee(*expr, db),
|
||||
}
|
||||
}
|
||||
Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db),
|
||||
Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db),
|
||||
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind() {
|
||||
TyKind::Adt(adt, ..) if matches!(adt.def_id().0, AdtId::UnionId(_)) => false,
|
||||
_ => self.is_known_valid_scrutinee(*expr),
|
||||
},
|
||||
Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base),
|
||||
Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr),
|
||||
Expr::Missing => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
|
||||
fn validate_block(&mut self, expr: &Expr) {
|
||||
let (Expr::Block { statements, .. }
|
||||
| Expr::Async { statements, .. }
|
||||
| Expr::Unsafe { statements, .. }) = expr
|
||||
|
|
@ -332,7 +332,7 @@ impl<'db> ExprValidator<'db> {
|
|||
return;
|
||||
};
|
||||
let pattern_arena = Arena::new();
|
||||
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
|
||||
let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env.clone());
|
||||
for stmt in &**statements {
|
||||
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
|
||||
continue;
|
||||
|
|
@ -342,13 +342,12 @@ impl<'db> ExprValidator<'db> {
|
|||
}
|
||||
let Some(initializer) = initializer else { continue };
|
||||
let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
|
||||
let ty = ty.to_chalk(self.interner);
|
||||
if ty.contains_unknown() {
|
||||
if ty.references_non_lt_error() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut have_errors = false;
|
||||
let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors);
|
||||
let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors);
|
||||
|
||||
// optimization, wildcard trivially hold
|
||||
if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
|
||||
|
|
@ -360,7 +359,7 @@ impl<'db> ExprValidator<'db> {
|
|||
has_guard: false,
|
||||
arm_data: (),
|
||||
};
|
||||
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) {
|
||||
let report = match cx.compute_match_usefulness(&[match_arm], ty, None) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
debug!(?e, "match usefulness error");
|
||||
|
|
@ -373,24 +372,23 @@ impl<'db> ExprValidator<'db> {
|
|||
pat,
|
||||
uncovered_patterns: missing_match_arms(
|
||||
&cx,
|
||||
&ty,
|
||||
ty,
|
||||
witnesses,
|
||||
false,
|
||||
self.owner.krate(db),
|
||||
self.owner.krate(self.db()),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_pattern<'p>(
|
||||
fn lower_pattern<'a>(
|
||||
&self,
|
||||
cx: &MatchCheckCtx<'p>,
|
||||
cx: &MatchCheckCtx<'a, 'db>,
|
||||
pat: PatId,
|
||||
db: &dyn HirDatabase,
|
||||
have_errors: &mut bool,
|
||||
) -> DeconstructedPat<'p> {
|
||||
let mut patcx = match_check::PatCtxt::new(db, &self.infer, &self.body);
|
||||
) -> DeconstructedPat<'a, 'db> {
|
||||
let mut patcx = match_check::PatCtxt::new(self.db(), &self.infer, &self.body);
|
||||
let pattern = patcx.lower_pattern(pat);
|
||||
let pattern = cx.lower_pat(&pattern);
|
||||
if !patcx.errors.is_empty() {
|
||||
|
|
@ -434,7 +432,7 @@ impl<'db> ExprValidator<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, db: &dyn HirDatabase) {
|
||||
fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr) {
|
||||
if !self.validate_lints {
|
||||
return;
|
||||
}
|
||||
|
|
@ -453,11 +451,11 @@ impl<'db> ExprValidator<'db> {
|
|||
&& last_then_expr_ty.is_never()
|
||||
{
|
||||
// Only look at sources if the then branch diverges and we have an else branch.
|
||||
let source_map = db.body_with_source_map(self.owner).1;
|
||||
let source_map = self.db().body_with_source_map(self.owner).1;
|
||||
let Ok(source_ptr) = source_map.expr_syntax(id) else {
|
||||
return;
|
||||
};
|
||||
let root = source_ptr.file_syntax(db);
|
||||
let root = source_ptr.file_syntax(self.db());
|
||||
let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root)
|
||||
else {
|
||||
return;
|
||||
|
|
@ -491,15 +489,15 @@ impl<'db> ExprValidator<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
struct FilterMapNextChecker {
|
||||
struct FilterMapNextChecker<'db> {
|
||||
filter_map_function_id: Option<hir_def::FunctionId>,
|
||||
next_function_id: Option<hir_def::FunctionId>,
|
||||
prev_filter_map_expr_id: Option<ExprId>,
|
||||
prev_receiver_ty: Option<chalk_ir::Ty<Interner>>,
|
||||
prev_receiver_ty: Option<Ty<'db>>,
|
||||
}
|
||||
|
||||
impl FilterMapNextChecker {
|
||||
fn new(resolver: &hir_def::resolver::Resolver<'_>, db: &dyn HirDatabase) -> Self {
|
||||
impl<'db> FilterMapNextChecker<'db> {
|
||||
fn new(resolver: &hir_def::resolver::Resolver<'db>, db: &'db dyn HirDatabase) -> Self {
|
||||
// Find and store the FunctionIds for Iterator::filter_map and Iterator::next
|
||||
let (next_function_id, filter_map_function_id) = match LangItem::IteratorNext
|
||||
.resolve_function(db, resolver.krate())
|
||||
|
|
@ -639,15 +637,19 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
|
|||
!has_type_mismatches
|
||||
}
|
||||
|
||||
fn missing_match_arms<'p>(
|
||||
cx: &MatchCheckCtx<'p>,
|
||||
scrut_ty: &Ty,
|
||||
witnesses: Vec<WitnessPat<'p>>,
|
||||
fn missing_match_arms<'a, 'db>(
|
||||
cx: &MatchCheckCtx<'a, 'db>,
|
||||
scrut_ty: Ty<'a>,
|
||||
witnesses: Vec<WitnessPat<'a, 'db>>,
|
||||
arms_is_empty: bool,
|
||||
krate: Crate,
|
||||
) -> String {
|
||||
struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, DisplayTarget);
|
||||
impl fmt::Display for DisplayWitness<'_, '_> {
|
||||
struct DisplayWitness<'a, 'b, 'db>(
|
||||
&'a WitnessPat<'b, 'db>,
|
||||
&'a MatchCheckCtx<'b, 'db>,
|
||||
DisplayTarget,
|
||||
);
|
||||
impl fmt::Display for DisplayWitness<'_, '_, '_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let DisplayWitness(witness, cx, display_target) = *self;
|
||||
let pat = cx.hoist_witness_pat(witness);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ mod pat_util;
|
|||
|
||||
pub(crate) mod pat_analysis;
|
||||
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
AdtId, EnumVariantId, LocalFieldId, Lookup, VariantId,
|
||||
expr_store::{Body, path::Path},
|
||||
|
|
@ -17,16 +16,16 @@ use hir_def::{
|
|||
item_tree::FieldsShape,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use rustc_type_ir::inherent::{IntoKind, SliceLike};
|
||||
use span::Edition;
|
||||
use stdx::{always, never};
|
||||
|
||||
use crate::next_solver::DbInterner;
|
||||
use crate::next_solver::mapping::NextSolverToChalk;
|
||||
use crate::{
|
||||
InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
|
||||
InferenceResult,
|
||||
db::HirDatabase,
|
||||
display::{HirDisplay, HirDisplayError, HirFormatter},
|
||||
infer::BindingMode,
|
||||
next_solver::{GenericArgs, Mutability, Ty, TyKind},
|
||||
};
|
||||
|
||||
use self::pat_util::EnumerateAndAdjustIterator;
|
||||
|
|
@ -41,46 +40,46 @@ pub(crate) enum PatternError {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct FieldPat {
|
||||
pub(crate) struct FieldPat<'db> {
|
||||
pub(crate) field: LocalFieldId,
|
||||
pub(crate) pattern: Pat,
|
||||
pub(crate) pattern: Pat<'db>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct Pat {
|
||||
pub(crate) ty: Ty,
|
||||
pub(crate) kind: Box<PatKind>,
|
||||
pub(crate) struct Pat<'db> {
|
||||
pub(crate) ty: Ty<'db>,
|
||||
pub(crate) kind: Box<PatKind<'db>>,
|
||||
}
|
||||
|
||||
/// Close relative to `rustc_mir_build::thir::pattern::PatKind`
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) enum PatKind {
|
||||
pub(crate) enum PatKind<'db> {
|
||||
Wild,
|
||||
Never,
|
||||
|
||||
/// `x`, `ref x`, `x @ P`, etc.
|
||||
Binding {
|
||||
name: Name,
|
||||
subpattern: Option<Pat>,
|
||||
subpattern: Option<Pat<'db>>,
|
||||
},
|
||||
|
||||
/// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with
|
||||
/// multiple variants.
|
||||
Variant {
|
||||
substs: Substitution,
|
||||
substs: GenericArgs<'db>,
|
||||
enum_variant: EnumVariantId,
|
||||
subpatterns: Vec<FieldPat>,
|
||||
subpatterns: Vec<FieldPat<'db>>,
|
||||
},
|
||||
|
||||
/// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with
|
||||
/// a single variant.
|
||||
Leaf {
|
||||
subpatterns: Vec<FieldPat>,
|
||||
subpatterns: Vec<FieldPat<'db>>,
|
||||
},
|
||||
|
||||
/// `&P`, `&mut P`, etc.
|
||||
Deref {
|
||||
subpattern: Pat,
|
||||
subpattern: Pat<'db>,
|
||||
},
|
||||
|
||||
// FIXME: for now, only bool literals are implemented
|
||||
|
|
@ -91,28 +90,27 @@ pub(crate) enum PatKind {
|
|||
/// An or-pattern, e.g. `p | q`.
|
||||
/// Invariant: `pats.len() >= 2`.
|
||||
Or {
|
||||
pats: Vec<Pat>,
|
||||
pats: Vec<Pat<'db>>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) struct PatCtxt<'db> {
|
||||
pub(crate) struct PatCtxt<'a, 'db> {
|
||||
db: &'db dyn HirDatabase,
|
||||
infer: &'db InferenceResult<'db>,
|
||||
body: &'db Body,
|
||||
infer: &'a InferenceResult<'db>,
|
||||
body: &'a Body,
|
||||
pub(crate) errors: Vec<PatternError>,
|
||||
interner: DbInterner<'db>,
|
||||
}
|
||||
|
||||
impl<'a> PatCtxt<'a> {
|
||||
impl<'a, 'db> PatCtxt<'a, 'db> {
|
||||
pub(crate) fn new(
|
||||
db: &'a dyn HirDatabase,
|
||||
infer: &'a InferenceResult<'a>,
|
||||
db: &'db dyn HirDatabase,
|
||||
infer: &'a InferenceResult<'db>,
|
||||
body: &'a Body,
|
||||
) -> Self {
|
||||
Self { db, infer, body, errors: Vec::new(), interner: DbInterner::new_with(db, None, None) }
|
||||
Self { db, infer, body, errors: Vec::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat {
|
||||
pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat<'db> {
|
||||
// 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.
|
||||
|
|
@ -121,15 +119,12 @@ impl<'a> PatCtxt<'a> {
|
|||
let unadjusted_pat = self.lower_pattern_unadjusted(pat);
|
||||
self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
|
||||
unadjusted_pat,
|
||||
|subpattern, ref_ty| Pat {
|
||||
ty: ref_ty.to_chalk(self.interner).clone(),
|
||||
kind: Box::new(PatKind::Deref { subpattern }),
|
||||
},
|
||||
|subpattern, ref_ty| Pat { ty: *ref_ty, kind: Box::new(PatKind::Deref { subpattern }) },
|
||||
)
|
||||
}
|
||||
|
||||
fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat {
|
||||
let mut ty = self.infer[pat].to_chalk(self.interner);
|
||||
fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat<'db> {
|
||||
let mut ty = self.infer[pat];
|
||||
let variant = self.infer.variant_resolution_for_pat(pat);
|
||||
|
||||
let kind = match self.body[pat] {
|
||||
|
|
@ -142,8 +137,8 @@ impl<'a> PatCtxt<'a> {
|
|||
}
|
||||
|
||||
hir_def::hir::Pat::Tuple { ref args, ellipsis } => {
|
||||
let arity = match *ty.kind(Interner) {
|
||||
TyKind::Tuple(arity, _) => arity,
|
||||
let arity = match ty.kind() {
|
||||
TyKind::Tuple(tys) => tys.len(),
|
||||
_ => {
|
||||
never!("unexpected type for tuple pattern: {:?}", ty);
|
||||
self.errors.push(PatternError::UnexpectedType);
|
||||
|
|
@ -156,10 +151,10 @@ impl<'a> PatCtxt<'a> {
|
|||
|
||||
hir_def::hir::Pat::Bind { id, subpat, .. } => {
|
||||
let bm = self.infer.binding_modes[pat];
|
||||
ty = self.infer[id].to_chalk(self.interner);
|
||||
ty = self.infer[id];
|
||||
let name = &self.body[id].name;
|
||||
match (bm, ty.kind(Interner)) {
|
||||
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty.clone(),
|
||||
match (bm, ty.kind()) {
|
||||
(BindingMode::Ref(_), TyKind::Ref(_, rty, _)) => ty = rty,
|
||||
(BindingMode::Ref(_), _) => {
|
||||
never!(
|
||||
"`ref {}` has wrong type {:?}",
|
||||
|
|
@ -167,7 +162,7 @@ impl<'a> PatCtxt<'a> {
|
|||
ty
|
||||
);
|
||||
self.errors.push(PatternError::UnexpectedType);
|
||||
return Pat { ty: ty.clone(), kind: PatKind::Wild.into() };
|
||||
return Pat { ty, kind: PatKind::Wild.into() };
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -177,7 +172,7 @@ impl<'a> PatCtxt<'a> {
|
|||
hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
|
||||
let expected_len = variant.unwrap().fields(self.db).fields().len();
|
||||
let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
|
||||
self.lower_variant_or_leaf(pat, &ty, subpatterns)
|
||||
self.lower_variant_or_leaf(pat, ty, subpatterns)
|
||||
}
|
||||
|
||||
hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => {
|
||||
|
|
@ -193,7 +188,7 @@ impl<'a> PatCtxt<'a> {
|
|||
})
|
||||
.collect();
|
||||
match subpatterns {
|
||||
Some(subpatterns) => self.lower_variant_or_leaf(pat, &ty, subpatterns),
|
||||
Some(subpatterns) => self.lower_variant_or_leaf(pat, ty, subpatterns),
|
||||
None => {
|
||||
self.errors.push(PatternError::MissingField);
|
||||
PatKind::Wild
|
||||
|
|
@ -213,7 +208,7 @@ impl<'a> PatCtxt<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
Pat { ty: ty.clone(), kind: Box::new(kind) }
|
||||
Pat { ty, kind: Box::new(kind) }
|
||||
}
|
||||
|
||||
fn lower_tuple_subpats(
|
||||
|
|
@ -221,7 +216,7 @@ impl<'a> PatCtxt<'a> {
|
|||
pats: &[PatId],
|
||||
expected_len: usize,
|
||||
ellipsis: Option<u32>,
|
||||
) -> Vec<FieldPat> {
|
||||
) -> Vec<FieldPat<'db>> {
|
||||
if pats.len() > expected_len {
|
||||
self.errors.push(PatternError::ExtraFields);
|
||||
return Vec::new();
|
||||
|
|
@ -236,28 +231,28 @@ impl<'a> PatCtxt<'a> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn lower_patterns(&mut self, pats: &[PatId]) -> Vec<Pat> {
|
||||
fn lower_patterns(&mut self, pats: &[PatId]) -> Vec<Pat<'db>> {
|
||||
pats.iter().map(|&p| self.lower_pattern(p)).collect()
|
||||
}
|
||||
|
||||
fn lower_opt_pattern(&mut self, pat: Option<PatId>) -> Option<Pat> {
|
||||
fn lower_opt_pattern(&mut self, pat: Option<PatId>) -> Option<Pat<'db>> {
|
||||
pat.map(|p| self.lower_pattern(p))
|
||||
}
|
||||
|
||||
fn lower_variant_or_leaf(
|
||||
&mut self,
|
||||
pat: PatId,
|
||||
ty: &Ty,
|
||||
subpatterns: Vec<FieldPat>,
|
||||
) -> PatKind {
|
||||
ty: Ty<'db>,
|
||||
subpatterns: Vec<FieldPat<'db>>,
|
||||
) -> PatKind<'db> {
|
||||
match self.infer.variant_resolution_for_pat(pat) {
|
||||
Some(variant_id) => {
|
||||
if let VariantId::EnumVariantId(enum_variant) = variant_id {
|
||||
let substs = match ty.kind(Interner) {
|
||||
TyKind::Adt(_, substs) => substs.clone(),
|
||||
let substs = match ty.kind() {
|
||||
TyKind::Adt(_, substs) => substs,
|
||||
kind => {
|
||||
always!(
|
||||
matches!(kind, TyKind::FnDef(..) | TyKind::Error),
|
||||
matches!(kind, TyKind::FnDef(..) | TyKind::Error(_)),
|
||||
"inappropriate type for def: {:?}",
|
||||
ty
|
||||
);
|
||||
|
|
@ -277,13 +272,13 @@ impl<'a> PatCtxt<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_path(&mut self, pat: PatId, _path: &Path) -> Pat {
|
||||
let ty = self.infer[pat].to_chalk(self.interner);
|
||||
fn lower_path(&mut self, pat: PatId, _path: &Path) -> Pat<'db> {
|
||||
let ty = self.infer[pat];
|
||||
|
||||
let pat_from_kind = |kind| Pat { ty: ty.clone(), kind: Box::new(kind) };
|
||||
let pat_from_kind = |kind| Pat { ty, kind: Box::new(kind) };
|
||||
|
||||
match self.infer.variant_resolution_for_pat(pat) {
|
||||
Some(_) => pat_from_kind(self.lower_variant_or_leaf(pat, &ty, Vec::new())),
|
||||
Some(_) => pat_from_kind(self.lower_variant_or_leaf(pat, ty, Vec::new())),
|
||||
None => {
|
||||
self.errors.push(PatternError::UnresolvedVariant);
|
||||
pat_from_kind(PatKind::Wild)
|
||||
|
|
@ -291,7 +286,7 @@ impl<'a> PatCtxt<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
|
||||
fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind<'db> {
|
||||
use hir_def::hir::{Expr, Literal::Bool};
|
||||
|
||||
match self.body[expr] {
|
||||
|
|
@ -304,7 +299,7 @@ impl<'a> PatCtxt<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Pat {
|
||||
impl HirDisplay for Pat<'_> {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
match &*self.kind {
|
||||
PatKind::Wild => write!(f, "_"),
|
||||
|
|
@ -402,7 +397,7 @@ impl HirDisplay for Pat {
|
|||
})
|
||||
});
|
||||
f.write_joined(subpats, ", ")?;
|
||||
if let (TyKind::Tuple(..), 1) = (self.ty.kind(Interner), num_fields) {
|
||||
if let (TyKind::Tuple(..), 1) = (self.ty.kind(), num_fields) {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
|
|
@ -411,8 +406,8 @@ impl HirDisplay for Pat {
|
|||
Ok(())
|
||||
}
|
||||
PatKind::Deref { subpattern } => {
|
||||
match self.ty.kind(Interner) {
|
||||
&TyKind::Ref(mutbl, ..) => {
|
||||
match self.ty.kind() {
|
||||
TyKind::Ref(.., mutbl) => {
|
||||
write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })?
|
||||
}
|
||||
_ => never!("{:?} is a bad Deref pattern type", self.ty),
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
//! Interface with `rustc_pattern_analysis`.
|
||||
|
||||
use std::cell::LazyCell;
|
||||
use std::fmt;
|
||||
use std::{cell::LazyCell, fmt};
|
||||
|
||||
use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
|
||||
use hir_def::{EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
|
||||
use intern::sym;
|
||||
use rustc_pattern_analysis::{
|
||||
IndexVec, PatCx, PrivateUninhabitedField,
|
||||
constructor::{Constructor, ConstructorSet, VariantVisibility},
|
||||
usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness},
|
||||
};
|
||||
use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind,
|
||||
TraitEnvironment,
|
||||
db::HirDatabase,
|
||||
infer::normalize,
|
||||
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
|
||||
next_solver::{
|
||||
DbInterner, TypingMode,
|
||||
infer::{DbInternerInferExt, InferCtxt},
|
||||
mapping::ChalkToNextSolver,
|
||||
Ty, TyKind,
|
||||
infer::{InferCtxt, traits::ObligationCause},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -31,10 +29,12 @@ use super::{FieldPat, Pat, PatKind};
|
|||
use Constructor::*;
|
||||
|
||||
// Re-export r-a-specific versions of all these types.
|
||||
pub(crate) type DeconstructedPat<'db> =
|
||||
rustc_pattern_analysis::pat::DeconstructedPat<MatchCheckCtx<'db>>;
|
||||
pub(crate) type MatchArm<'a, 'db> = rustc_pattern_analysis::MatchArm<'a, MatchCheckCtx<'db>>;
|
||||
pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'db>>;
|
||||
pub(crate) type DeconstructedPat<'a, 'db> =
|
||||
rustc_pattern_analysis::pat::DeconstructedPat<MatchCheckCtx<'a, 'db>>;
|
||||
pub(crate) type MatchArm<'a, 'b, 'db> =
|
||||
rustc_pattern_analysis::MatchArm<'b, MatchCheckCtx<'a, 'db>>;
|
||||
pub(crate) type WitnessPat<'a, 'db> =
|
||||
rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'a, 'db>>;
|
||||
|
||||
/// [Constructor] uses this in unimplemented variants.
|
||||
/// It allows porting match expressions from upstream algorithm without losing semantics.
|
||||
|
|
@ -70,40 +70,37 @@ impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct MatchCheckCtx<'db> {
|
||||
pub(crate) struct MatchCheckCtx<'a, 'db> {
|
||||
module: ModuleId,
|
||||
body: DefWithBodyId,
|
||||
pub(crate) db: &'db dyn HirDatabase,
|
||||
exhaustive_patterns: bool,
|
||||
env: Arc<TraitEnvironment<'db>>,
|
||||
infcx: InferCtxt<'db>,
|
||||
infcx: &'a InferCtxt<'db>,
|
||||
}
|
||||
|
||||
impl<'db> MatchCheckCtx<'db> {
|
||||
impl<'a, 'db> MatchCheckCtx<'a, 'db> {
|
||||
pub(crate) fn new(
|
||||
module: ModuleId,
|
||||
body: DefWithBodyId,
|
||||
db: &'db dyn HirDatabase,
|
||||
infcx: &'a InferCtxt<'db>,
|
||||
env: Arc<TraitEnvironment<'db>>,
|
||||
) -> Self {
|
||||
let db = infcx.interner.db;
|
||||
let def_map = module.crate_def_map(db);
|
||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
|
||||
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, body.into()));
|
||||
Self { module, body, db, exhaustive_patterns, env, infcx }
|
||||
Self { module, db, exhaustive_patterns, env, infcx }
|
||||
}
|
||||
|
||||
pub(crate) fn compute_match_usefulness<'a>(
|
||||
pub(crate) fn compute_match_usefulness<'b>(
|
||||
&self,
|
||||
arms: &[MatchArm<'a, 'db>],
|
||||
scrut_ty: Ty,
|
||||
arms: &[MatchArm<'a, 'b, 'db>],
|
||||
scrut_ty: Ty<'db>,
|
||||
known_valid_scrutinee: Option<bool>,
|
||||
) -> Result<UsefulnessReport<'a, Self>, ()> {
|
||||
if scrut_ty.contains_unknown() {
|
||||
) -> Result<UsefulnessReport<'b, Self>, ()> {
|
||||
if scrut_ty.references_non_lt_error() {
|
||||
return Err(());
|
||||
}
|
||||
for arm in arms {
|
||||
if arm.pat.ty().contains_unknown() {
|
||||
if arm.pat.ty().references_non_lt_error() {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
|
@ -114,13 +111,8 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
|
||||
}
|
||||
|
||||
fn is_uninhabited(&self, ty: &Ty) -> bool {
|
||||
is_ty_uninhabited_from(
|
||||
&self.infcx,
|
||||
ty.to_nextsolver(self.infcx.interner),
|
||||
self.module,
|
||||
self.env.clone(),
|
||||
)
|
||||
fn is_uninhabited(&self, ty: Ty<'db>) -> bool {
|
||||
is_ty_uninhabited_from(self.infcx, ty, self.module, self.env.clone())
|
||||
}
|
||||
|
||||
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
|
||||
|
|
@ -153,23 +145,27 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
// This lists the fields of a variant along with their types.
|
||||
fn list_variant_fields(
|
||||
&self,
|
||||
ty: &Ty,
|
||||
ty: Ty<'db>,
|
||||
variant: VariantId,
|
||||
) -> impl Iterator<Item = (LocalFieldId, Ty)> {
|
||||
) -> impl Iterator<Item = (LocalFieldId, Ty<'db>)> {
|
||||
let (_, substs) = ty.as_adt().unwrap();
|
||||
|
||||
let field_tys = self.db.field_types(variant);
|
||||
let field_tys = self.db.field_types_ns(variant);
|
||||
let fields_len = variant.fields(self.db).fields().len() as u32;
|
||||
|
||||
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| {
|
||||
let ty = field_tys[fid].clone().substitute(Interner, substs);
|
||||
let ty = normalize(self.db, self.db.trait_environment_for_body(self.body), ty);
|
||||
let ty = field_tys[fid].instantiate(self.infcx.interner, substs);
|
||||
let ty = self
|
||||
.infcx
|
||||
.at(&ObligationCause::dummy(), self.env.env)
|
||||
.deeply_normalize(ty)
|
||||
.unwrap_or(ty);
|
||||
(fid, ty)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'db> {
|
||||
let singleton = |pat: DeconstructedPat<'db>| vec![pat.at_index(0)];
|
||||
pub(crate) fn lower_pat(&self, pat: &Pat<'db>) -> DeconstructedPat<'a, 'db> {
|
||||
let singleton = |pat: DeconstructedPat<'a, 'db>| vec![pat.at_index(0)];
|
||||
let ctor;
|
||||
let mut fields: Vec<_>;
|
||||
let arity;
|
||||
|
|
@ -182,7 +178,7 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
arity = 0;
|
||||
}
|
||||
PatKind::Deref { subpattern } => {
|
||||
ctor = match pat.ty.kind(Interner) {
|
||||
ctor = match pat.ty.kind() {
|
||||
TyKind::Ref(..) => Ref,
|
||||
_ => {
|
||||
never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty);
|
||||
|
|
@ -200,12 +196,13 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
self.lower_pat(&pat.pattern).at_index(idx as usize)
|
||||
})
|
||||
.collect();
|
||||
match pat.ty.kind(Interner) {
|
||||
TyKind::Tuple(_, substs) => {
|
||||
match pat.ty.kind() {
|
||||
TyKind::Tuple(substs) => {
|
||||
ctor = Struct;
|
||||
arity = substs.len(Interner);
|
||||
arity = substs.len();
|
||||
}
|
||||
&TyKind::Adt(AdtId(adt), _) => {
|
||||
TyKind::Adt(adt_def, _) => {
|
||||
let adt = adt_def.def_id().0;
|
||||
ctor = match pat.kind.as_ref() {
|
||||
PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => {
|
||||
UnionField
|
||||
|
|
@ -253,15 +250,15 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
arity = pats.len();
|
||||
}
|
||||
}
|
||||
DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), ())
|
||||
DeconstructedPat::new(ctor, fields, arity, pat.ty, ())
|
||||
}
|
||||
|
||||
pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'db>) -> Pat {
|
||||
pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'a, 'db>) -> Pat<'db> {
|
||||
let mut subpatterns = pat.iter_fields().map(|p| self.hoist_witness_pat(p));
|
||||
let kind = match pat.ctor() {
|
||||
&Bool(value) => PatKind::LiteralBool { value },
|
||||
IntRange(_) => unimplemented!(),
|
||||
Struct | Variant(_) | UnionField => match pat.ty().kind(Interner) {
|
||||
Struct | Variant(_) | UnionField => match pat.ty().kind() {
|
||||
TyKind::Tuple(..) => PatKind::Leaf {
|
||||
subpatterns: subpatterns
|
||||
.zip(0u32..)
|
||||
|
|
@ -272,15 +269,16 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
.collect(),
|
||||
},
|
||||
TyKind::Adt(adt, substs) => {
|
||||
let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap();
|
||||
let variant =
|
||||
Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id().0).unwrap();
|
||||
let subpatterns = self
|
||||
.list_variant_fields(pat.ty(), variant)
|
||||
.list_variant_fields(*pat.ty(), variant)
|
||||
.zip(subpatterns)
|
||||
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
|
||||
.collect();
|
||||
|
||||
if let VariantId::EnumVariantId(enum_variant) = variant {
|
||||
PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns }
|
||||
PatKind::Variant { substs, enum_variant, subpatterns }
|
||||
} else {
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
|
|
@ -306,13 +304,13 @@ impl<'db> MatchCheckCtx<'db> {
|
|||
PatKind::Wild
|
||||
}
|
||||
};
|
||||
Pat { ty: pat.ty().clone(), kind: Box::new(kind) }
|
||||
Pat { ty: *pat.ty(), kind: Box::new(kind) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PatCx for MatchCheckCtx<'_> {
|
||||
impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> {
|
||||
type Error = ();
|
||||
type Ty = Ty;
|
||||
type Ty = Ty<'db>;
|
||||
type VariantIdx = EnumVariantContiguousIndex;
|
||||
type StrLit = Void;
|
||||
type ArmData = ();
|
||||
|
|
@ -328,10 +326,11 @@ impl PatCx for MatchCheckCtx<'_> {
|
|||
ty: &Self::Ty,
|
||||
) -> usize {
|
||||
match ctor {
|
||||
Struct | Variant(_) | UnionField => match *ty.kind(Interner) {
|
||||
TyKind::Tuple(arity, ..) => arity,
|
||||
TyKind::Adt(AdtId(adt), ..) => {
|
||||
let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
|
||||
Struct | Variant(_) | UnionField => match ty.kind() {
|
||||
TyKind::Tuple(tys) => tys.len(),
|
||||
TyKind::Adt(adt_def, ..) => {
|
||||
let variant =
|
||||
Self::variant_id_for_adt(self.db, ctor, adt_def.def_id().0).unwrap();
|
||||
variant.fields(self.db).fields().len()
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -359,24 +358,24 @@ impl PatCx for MatchCheckCtx<'_> {
|
|||
) -> impl ExactSizeIterator<Item = (Self::Ty, PrivateUninhabitedField)> {
|
||||
let single = |ty| smallvec![(ty, PrivateUninhabitedField(false))];
|
||||
let tys: SmallVec<[_; 2]> = match ctor {
|
||||
Struct | Variant(_) | UnionField => match ty.kind(Interner) {
|
||||
TyKind::Tuple(_, substs) => {
|
||||
let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner));
|
||||
tys.cloned().map(|ty| (ty, PrivateUninhabitedField(false))).collect()
|
||||
Struct | Variant(_) | UnionField => match ty.kind() {
|
||||
TyKind::Tuple(substs) => {
|
||||
substs.iter().map(|ty| (ty, PrivateUninhabitedField(false))).collect()
|
||||
}
|
||||
TyKind::Ref(.., rty) => single(rty.clone()),
|
||||
&TyKind::Adt(AdtId(adt), ..) => {
|
||||
TyKind::Ref(_, rty, _) => single(rty),
|
||||
TyKind::Adt(adt_def, ..) => {
|
||||
let adt = adt_def.def_id().0;
|
||||
let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
|
||||
|
||||
let visibilities = LazyCell::new(|| self.db.field_visibilities(variant));
|
||||
|
||||
self.list_variant_fields(ty, variant)
|
||||
self.list_variant_fields(*ty, variant)
|
||||
.map(move |(fid, ty)| {
|
||||
let is_visible = || {
|
||||
matches!(adt, hir_def::AdtId::EnumId(..))
|
||||
|| visibilities[fid].is_visible_from(self.db, self.module)
|
||||
};
|
||||
let is_uninhabited = self.is_uninhabited(&ty);
|
||||
let is_uninhabited = self.is_uninhabited(ty);
|
||||
let private_uninhabited = is_uninhabited && !is_visible();
|
||||
(ty, PrivateUninhabitedField(private_uninhabited))
|
||||
})
|
||||
|
|
@ -384,14 +383,14 @@ impl PatCx for MatchCheckCtx<'_> {
|
|||
}
|
||||
ty_kind => {
|
||||
never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
|
||||
single(ty.clone())
|
||||
single(*ty)
|
||||
}
|
||||
},
|
||||
Ref => match ty.kind(Interner) {
|
||||
TyKind::Ref(.., rty) => single(rty.clone()),
|
||||
Ref => match ty.kind() {
|
||||
TyKind::Ref(_, rty, _) => single(rty),
|
||||
ty_kind => {
|
||||
never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
|
||||
single(ty.clone())
|
||||
single(*ty)
|
||||
}
|
||||
},
|
||||
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
|
||||
|
|
@ -427,42 +426,51 @@ impl PatCx for MatchCheckCtx<'_> {
|
|||
// returned list of constructors.
|
||||
// Invariant: this is empty if and only if the type is uninhabited (as determined by
|
||||
// `cx.is_uninhabited()`).
|
||||
Ok(match ty.kind(Interner) {
|
||||
TyKind::Scalar(Scalar::Bool) => ConstructorSet::Bool,
|
||||
TyKind::Scalar(Scalar::Char) => unhandled(),
|
||||
TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
|
||||
Ok(match ty.kind() {
|
||||
TyKind::Bool => ConstructorSet::Bool,
|
||||
TyKind::Char => unhandled(),
|
||||
TyKind::Int(..) | TyKind::Uint(..) => unhandled(),
|
||||
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
|
||||
&TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
|
||||
let enum_data = enum_id.enum_variants(cx.db);
|
||||
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
|
||||
TyKind::Adt(adt_def, subst) => {
|
||||
let adt = adt_def.def_id().0;
|
||||
match adt {
|
||||
hir_def::AdtId::EnumId(enum_id) => {
|
||||
let enum_data = enum_id.enum_variants(cx.db);
|
||||
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
|
||||
|
||||
if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
|
||||
ConstructorSet::NoConstructors
|
||||
} else {
|
||||
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
|
||||
for &(variant, _, _) in enum_data.variants.iter() {
|
||||
let is_uninhabited = is_enum_variant_uninhabited_from(
|
||||
&cx.infcx,
|
||||
variant,
|
||||
subst.to_nextsolver(cx.infcx.interner),
|
||||
cx.module,
|
||||
self.env.clone(),
|
||||
);
|
||||
let visibility = if is_uninhabited {
|
||||
VariantVisibility::Empty
|
||||
if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
|
||||
ConstructorSet::NoConstructors
|
||||
} else {
|
||||
VariantVisibility::Visible
|
||||
};
|
||||
variants.push(visibility);
|
||||
}
|
||||
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
|
||||
for &(variant, _, _) in enum_data.variants.iter() {
|
||||
let is_uninhabited = is_enum_variant_uninhabited_from(
|
||||
cx.infcx,
|
||||
variant,
|
||||
subst,
|
||||
cx.module,
|
||||
self.env.clone(),
|
||||
);
|
||||
let visibility = if is_uninhabited {
|
||||
VariantVisibility::Empty
|
||||
} else {
|
||||
VariantVisibility::Visible
|
||||
};
|
||||
variants.push(visibility);
|
||||
}
|
||||
|
||||
ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
|
||||
ConstructorSet::Variants {
|
||||
variants,
|
||||
non_exhaustive: is_declared_nonexhaustive,
|
||||
}
|
||||
}
|
||||
}
|
||||
hir_def::AdtId::UnionId(_) => ConstructorSet::Union,
|
||||
hir_def::AdtId::StructId(_) => {
|
||||
ConstructorSet::Struct { empty: cx.is_uninhabited(*ty) }
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::Adt(AdtId(hir_def::AdtId::UnionId(_)), _) => ConstructorSet::Union,
|
||||
TyKind::Adt(..) | TyKind::Tuple(..) => {
|
||||
ConstructorSet::Struct { empty: cx.is_uninhabited(ty) }
|
||||
}
|
||||
TyKind::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(*ty) },
|
||||
TyKind::Ref(..) => ConstructorSet::Ref,
|
||||
TyKind::Never => ConstructorSet::NoConstructors,
|
||||
// This type is one for which we cannot list constructors, like `str` or `f64`.
|
||||
|
|
@ -505,14 +513,14 @@ impl PatCx for MatchCheckCtx<'_> {
|
|||
|
||||
fn report_mixed_deref_pat_ctors(
|
||||
&self,
|
||||
_deref_pat: &DeconstructedPat<'_>,
|
||||
_normal_pat: &DeconstructedPat<'_>,
|
||||
_deref_pat: &DeconstructedPat<'a, 'db>,
|
||||
_normal_pat: &DeconstructedPat<'a, 'db>,
|
||||
) {
|
||||
// FIXME(deref_patterns): This could report an error comparable to the one in rustc.
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MatchCheckCtx<'_> {
|
||||
impl fmt::Debug for MatchCheckCtx<'_, '_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MatchCheckCtx").finish()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ pub use utils::{
|
|||
};
|
||||
pub use variance::Variance;
|
||||
|
||||
use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Safety, Scalar};
|
||||
use chalk_ir::{BoundVar, DebruijnIndex, Safety, Scalar};
|
||||
|
||||
pub(crate) type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
|
||||
pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue