From 5736f47b032a2d4021ee3b1084689d4e084d0a4e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 12:25:14 +0300 Subject: [PATCH] Migrate hir-ty body validation diagnostics to the next solver --- .../crates/hir-ty/src/chalk_ext.rs | 59 +---- .../crates/hir-ty/src/diagnostics/expr.rs | 154 +++++++------ .../hir-ty/src/diagnostics/match_check.rs | 111 +++++---- .../diagnostics/match_check/pat_analysis.rs | 216 +++++++++--------- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- 5 files changed, 247 insertions(+), 295 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index e9960374c6f5..ea3ed1589d75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -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; - fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; fn callable_def(&self, db: &dyn HirDatabase) -> Option; fn callable_sig(&self, db: &dyn HirDatabase) -> Option; - - fn strip_references(&self) -> &Ty; - - /// If this is a `dyn Trait`, returns that trait. - fn dyn_trait(&self) -> Option; } 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 { 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 { - 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 { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 7b6fb994ecaf..0eca0c09d690 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -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>, diagnostics: Vec, 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, + filter_map_next_checker: &mut Option>, ) { 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, next_function_id: Option, prev_filter_map_expr_id: Option, - prev_receiver_ty: Option>, + prev_receiver_ty: Option>, } -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>, +fn missing_match_arms<'a, 'db>( + cx: &MatchCheckCtx<'a, 'db>, + scrut_ty: Ty<'a>, + witnesses: Vec>, 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); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index af541ffa342e..af6795e6018a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -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, +pub(crate) struct Pat<'db> { + pub(crate) ty: Ty<'db>, + pub(crate) kind: Box>, } /// 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, + subpattern: Option>, }, /// `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, + subpatterns: Vec>, }, /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with /// a single variant. Leaf { - subpatterns: Vec, + subpatterns: Vec>, }, /// `&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, + pats: Vec>, }, } -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, - 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, - ) -> Vec { + ) -> Vec> { 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 { + fn lower_patterns(&mut self, pats: &[PatId]) -> Vec> { pats.iter().map(|&p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern(&mut self, pat: Option) -> Option { + fn lower_opt_pattern(&mut self, pat: Option) -> Option> { pat.map(|p| self.lower_pattern(p)) } fn lower_variant_or_leaf( &mut self, pat: PatId, - ty: &Ty, - subpatterns: Vec, - ) -> PatKind { + ty: Ty<'db>, + subpatterns: Vec>, + ) -> 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), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 76f50c194835..f0efadeafcea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -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>; -pub(crate) type MatchArm<'a, 'db> = rustc_pattern_analysis::MatchArm<'a, MatchCheckCtx<'db>>; -pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat>; +pub(crate) type DeconstructedPat<'a, 'db> = + rustc_pattern_analysis::pat::DeconstructedPat>; +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>; /// [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>, - 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>, ) -> 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, - ) -> Result, ()> { - if scrut_ty.contains_unknown() { + ) -> Result, ()> { + 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 { + ) -> impl Iterator)> { 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 { 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() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 734483a823e4..eb01ef104b61 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -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; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId;