Migrate hir-ty body validation diagnostics to the next solver

This commit is contained in:
Chayim Refael Friedman 2025-10-12 12:25:14 +03:00
parent 92eef5348a
commit 5736f47b03
5 changed files with 247 additions and 295 deletions

View file

@ -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 {

View file

@ -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);

View file

@ -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),

View file

@ -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()
}

View file

@ -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>;