Skip to content

Commit 5038446

Browse files
Rewrite method resolution to follow rustc more closely
It cannot be exactly the same, because we have needs rustc doesn't have (namely, accurate enumeration of all methods, not just with a specific name, for completions etc., while rustc also needs a best-effort implementation for diagnostics) but it is closer than the previous impl. In addition we rewrite the closely related handling of operator inference and impl collection. This in turn necessitate changing some other parts of inference in order to retain behavior. As a result, the behavior more closely matches rustc and is also more correct. This fixes 2 type mismatches on self (1 remains) and 4 diagnostics (1 remains), plus some unknown types.
1 parent 5ffe3f4 commit 5038446

File tree

90 files changed

+7322
-4159
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+7322
-4159
lines changed

crates/base-db/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ pub trait RootQueryDb: SourceDatabase + salsa::Database {
273273
fn transitive_rev_deps(&self, of: Crate) -> FxHashSet<Crate>;
274274
}
275275

276-
pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
276+
fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
277277
// There is a bit of duplication here and in `CrateGraphBuilder` in the same method, but it's not terrible
278278
// and removing that is a bit difficult.
279279
let mut worklist = vec![crate_id];

crates/hir-def/src/resolver.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,20 @@ impl<'db> Resolver<'db> {
708708
self.item_scope_().0
709709
}
710710

711+
#[inline]
712+
pub fn top_level_def_map(&self) -> &'db DefMap {
713+
self.module_scope.def_map
714+
}
715+
716+
#[inline]
717+
pub fn is_visible(&self, db: &dyn DefDatabase, visibility: Visibility) -> bool {
718+
visibility.is_visible_from_def_map(
719+
db,
720+
self.module_scope.def_map,
721+
self.module_scope.module_id,
722+
)
723+
}
724+
711725
pub fn generic_def(&self) -> Option<GenericDefId> {
712726
self.scopes().find_map(|scope| match scope {
713727
Scope::GenericParams { def, .. } => Some(*def),

crates/hir-ty/src/autoderef.rs

Lines changed: 150 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use triomphe::Arc;
1313
use crate::{
1414
TraitEnvironment,
1515
db::HirDatabase,
16-
infer::unify::InferenceTable,
16+
infer::InferenceContext,
1717
next_solver::{
18-
Canonical, TraitRef, Ty, TyKind,
18+
Canonical, DbInterner, ParamEnv, TraitRef, Ty, TyKind, TypingMode,
1919
infer::{
20-
InferOk,
20+
DbInternerInferExt, InferCtxt,
2121
traits::{Obligation, ObligationCause, PredicateObligations},
2222
},
2323
obligation_ctxt::ObligationCtxt,
@@ -38,14 +38,15 @@ pub fn autoderef<'db>(
3838
env: Arc<TraitEnvironment<'db>>,
3939
ty: Canonical<'db, Ty<'db>>,
4040
) -> impl Iterator<Item = Ty<'db>> + use<'db> {
41-
let mut table = InferenceTable::new(db, env, None);
42-
let ty = table.instantiate_canonical(ty);
43-
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
41+
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
42+
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
43+
let (ty, _) = infcx.instantiate_canonical(&ty);
44+
let autoderef = Autoderef::new(&infcx, &env, ty);
4445
let mut v = Vec::new();
45-
while let Some((ty, _steps)) = autoderef.next() {
46+
for (ty, _steps) in autoderef {
4647
// `ty` may contain unresolved inference variables. Since there's no chance they would be
4748
// resolved, just replace with fallback type.
48-
let resolved = autoderef.table.resolve_completely(ty);
49+
let resolved = infcx.resolve_vars_if_possible(ty).replace_infer_with_error(interner);
4950

5051
// If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
5152
// would revisit some already visited types. Stop here to avoid duplication.
@@ -105,13 +106,48 @@ struct AutoderefTraits {
105106
trait_target: TypeAliasId,
106107
}
107108

109+
// We use a trait here and a generic implementation unfortunately, because sometimes (specifically
110+
// in place_op.rs), you need to have mutable access to the `InferenceContext` while the `Autoderef`
111+
// borrows it.
112+
pub(crate) trait AutoderefCtx<'db> {
113+
fn infcx(&self) -> &InferCtxt<'db>;
114+
fn env(&self) -> &TraitEnvironment<'db>;
115+
}
116+
117+
pub(crate) struct DefaultAutoderefCtx<'a, 'db> {
118+
infcx: &'a InferCtxt<'db>,
119+
env: &'a TraitEnvironment<'db>,
120+
}
121+
impl<'db> AutoderefCtx<'db> for DefaultAutoderefCtx<'_, 'db> {
122+
#[inline]
123+
fn infcx(&self) -> &InferCtxt<'db> {
124+
self.infcx
125+
}
126+
#[inline]
127+
fn env(&self) -> &TraitEnvironment<'db> {
128+
self.env
129+
}
130+
}
131+
132+
pub(crate) struct InferenceContextAutoderefCtx<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>);
133+
impl<'db> AutoderefCtx<'db> for InferenceContextAutoderefCtx<'_, '_, 'db> {
134+
#[inline]
135+
fn infcx(&self) -> &InferCtxt<'db> {
136+
&self.0.table.infer_ctxt
137+
}
138+
#[inline]
139+
fn env(&self) -> &TraitEnvironment<'db> {
140+
&self.0.table.trait_env
141+
}
142+
}
143+
108144
/// Recursively dereference a type, considering both built-in
109145
/// dereferences (`*`) and the `Deref` trait.
110146
/// Although called `Autoderef` it can be configured to use the
111147
/// `Receiver` trait instead of the `Deref` trait.
112-
pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
148+
pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
113149
// Meta infos:
114-
pub(crate) table: &'a mut InferenceTable<'db>,
150+
ctx: Ctx,
115151
traits: Option<AutoderefTraits>,
116152

117153
// Current state:
@@ -122,7 +158,16 @@ pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
122158
use_receiver_trait: bool,
123159
}
124160

125-
impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, Steps> {
161+
pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
162+
GeneralAutoderef<'db, DefaultAutoderefCtx<'a, 'db>, Steps>;
163+
pub(crate) type InferenceContextAutoderef<'a, 'b, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
164+
GeneralAutoderef<'db, InferenceContextAutoderefCtx<'a, 'b, 'db>, Steps>;
165+
166+
impl<'db, Ctx, Steps> Iterator for GeneralAutoderef<'db, Ctx, Steps>
167+
where
168+
Ctx: AutoderefCtx<'db>,
169+
Steps: TrackAutoderefSteps<'db>,
170+
{
126171
type Item = (Ty<'db>, usize);
127172

128173
fn next(&mut self) -> Option<Self::Item> {
@@ -148,26 +193,26 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, S
148193
// be better to skip this clause and use the Overloaded case only, since &T
149194
// and &mut T implement Receiver. But built-in derefs apply equally to Receiver
150195
// and Deref, and this has benefits for const and the emitted MIR.
151-
let (kind, new_ty) = if let Some(ty) =
152-
self.state.cur_ty.builtin_deref(self.table.db, self.include_raw_pointers)
153-
{
154-
debug_assert_eq!(ty, self.table.infer_ctxt.resolve_vars_if_possible(ty));
155-
// NOTE: we may still need to normalize the built-in deref in case
156-
// we have some type like `&<Ty as Trait>::Assoc`, since users of
157-
// autoderef expect this type to have been structurally normalized.
158-
if let TyKind::Alias(..) = ty.kind() {
159-
let (normalized_ty, obligations) = structurally_normalize_ty(self.table, ty)?;
160-
self.state.obligations.extend(obligations);
161-
(AutoderefKind::Builtin, normalized_ty)
196+
let (kind, new_ty) =
197+
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
198+
debug_assert_eq!(ty, self.infcx().resolve_vars_if_possible(ty));
199+
// NOTE: we may still need to normalize the built-in deref in case
200+
// we have some type like `&<Ty as Trait>::Assoc`, since users of
201+
// autoderef expect this type to have been structurally normalized.
202+
if let TyKind::Alias(..) = ty.kind() {
203+
let (normalized_ty, obligations) =
204+
structurally_normalize_ty(self.infcx(), self.env().env, ty)?;
205+
self.state.obligations.extend(obligations);
206+
(AutoderefKind::Builtin, normalized_ty)
207+
} else {
208+
(AutoderefKind::Builtin, ty)
209+
}
210+
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
211+
// The overloaded deref check already normalizes the pointee type.
212+
(AutoderefKind::Overloaded, ty)
162213
} else {
163-
(AutoderefKind::Builtin, ty)
164-
}
165-
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
166-
// The overloaded deref check already normalizes the pointee type.
167-
(AutoderefKind::Overloaded, ty)
168-
} else {
169-
return None;
170-
};
214+
return None;
215+
};
171216

172217
self.state.steps.push(self.state.cur_ty, kind);
173218
debug!(
@@ -183,34 +228,84 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, S
183228
}
184229

185230
impl<'a, 'db> Autoderef<'a, 'db> {
186-
pub(crate) fn new(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
187-
Self::new_impl(table, base_ty)
231+
#[inline]
232+
pub(crate) fn new_with_tracking(
233+
infcx: &'a InferCtxt<'db>,
234+
env: &'a TraitEnvironment<'db>,
235+
base_ty: Ty<'db>,
236+
) -> Self {
237+
Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty)
238+
}
239+
}
240+
241+
impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> {
242+
#[inline]
243+
pub(crate) fn new_from_inference_context(
244+
ctx: &'a mut InferenceContext<'b, 'db>,
245+
base_ty: Ty<'db>,
246+
) -> Self {
247+
Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty)
248+
}
249+
250+
#[inline]
251+
pub(crate) fn ctx(&mut self) -> &mut InferenceContext<'b, 'db> {
252+
self.ctx.0
188253
}
189254
}
190255

191256
impl<'a, 'db> Autoderef<'a, 'db, usize> {
192-
pub(crate) fn new_no_tracking(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
193-
Self::new_impl(table, base_ty)
257+
#[inline]
258+
pub(crate) fn new(
259+
infcx: &'a InferCtxt<'db>,
260+
env: &'a TraitEnvironment<'db>,
261+
base_ty: Ty<'db>,
262+
) -> Self {
263+
Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty)
194264
}
195265
}
196266

197-
impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
198-
fn new_impl(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
199-
Autoderef {
267+
impl<'db, Ctx, Steps> GeneralAutoderef<'db, Ctx, Steps>
268+
where
269+
Ctx: AutoderefCtx<'db>,
270+
Steps: TrackAutoderefSteps<'db>,
271+
{
272+
#[inline]
273+
fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self {
274+
GeneralAutoderef {
200275
state: AutoderefSnapshot {
201276
steps: Steps::default(),
202-
cur_ty: table.infer_ctxt.resolve_vars_if_possible(base_ty),
277+
cur_ty: ctx.infcx().resolve_vars_if_possible(base_ty),
203278
obligations: PredicateObligations::new(),
204279
at_start: true,
205280
reached_recursion_limit: false,
206281
},
207-
table,
282+
ctx,
208283
traits: None,
209284
include_raw_pointers: false,
210285
use_receiver_trait: false,
211286
}
212287
}
213288

289+
#[inline]
290+
fn infcx(&self) -> &InferCtxt<'db> {
291+
self.ctx.infcx()
292+
}
293+
294+
#[inline]
295+
fn env(&self) -> &TraitEnvironment<'db> {
296+
self.ctx.env()
297+
}
298+
299+
#[inline]
300+
fn interner(&self) -> DbInterner<'db> {
301+
self.infcx().interner
302+
}
303+
304+
#[inline]
305+
fn db(&self) -> &'db dyn HirDatabase {
306+
self.interner().db
307+
}
308+
214309
fn autoderef_traits(&mut self) -> Option<AutoderefTraits> {
215310
match &mut self.traits {
216311
Some(it) => Some(*it),
@@ -219,25 +314,23 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
219314
(|| {
220315
Some(AutoderefTraits {
221316
trait_: LangItem::Receiver
222-
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
317+
.resolve_trait(self.db(), self.env().krate)?,
223318
trait_target: LangItem::ReceiverTarget
224-
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
319+
.resolve_type_alias(self.db(), self.env().krate)?,
225320
})
226321
})()
227322
.or_else(|| {
228323
Some(AutoderefTraits {
229-
trait_: LangItem::Deref
230-
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
324+
trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?,
231325
trait_target: LangItem::DerefTarget
232-
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
326+
.resolve_type_alias(self.db(), self.env().krate)?,
233327
})
234328
})?
235329
} else {
236330
AutoderefTraits {
237-
trait_: LangItem::Deref
238-
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
331+
trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?,
239332
trait_target: LangItem::DerefTarget
240-
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
333+
.resolve_type_alias(self.db(), self.env().krate)?,
241334
}
242335
};
243336
Some(*self.traits.insert(traits))
@@ -247,31 +340,32 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
247340

248341
fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option<Ty<'db>> {
249342
debug!("overloaded_deref_ty({:?})", ty);
250-
let interner = self.table.interner();
343+
let interner = self.interner();
251344

252345
// <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk.
253346
let AutoderefTraits { trait_, trait_target } = self.autoderef_traits()?;
254347

255348
let trait_ref = TraitRef::new(interner, trait_.into(), [ty]);
256349
let obligation =
257-
Obligation::new(interner, ObligationCause::new(), self.table.trait_env.env, trait_ref);
350+
Obligation::new(interner, ObligationCause::new(), self.env().env, trait_ref);
258351
// We detect whether the self type implements `Deref` before trying to
259352
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
260353
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
261354
// but fail for `impl OtherTrait`.
262-
if !self.table.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation) {
355+
if !self.infcx().predicate_may_hold_opaque_types_jank(&obligation) {
263356
debug!("overloaded_deref_ty: cannot match obligation");
264357
return None;
265358
}
266359

267360
let (normalized_ty, obligations) = structurally_normalize_ty(
268-
self.table,
361+
self.infcx(),
362+
self.env().env,
269363
Ty::new_projection(interner, trait_target.into(), [ty]),
270364
)?;
271365
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
272366
self.state.obligations.extend(obligations);
273367

274-
Some(self.table.infer_ctxt.resolve_vars_if_possible(normalized_ty))
368+
Some(self.infcx().resolve_vars_if_possible(normalized_ty))
275369
}
276370

277371
/// Returns the final type we ended up with, which may be an unresolved
@@ -292,7 +386,6 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
292386
&self.state.steps
293387
}
294388

295-
#[expect(dead_code)]
296389
pub(crate) fn reached_recursion_limit(&self) -> bool {
297390
self.state.reached_recursion_limit
298391
}
@@ -316,12 +409,12 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
316409
}
317410

318411
fn structurally_normalize_ty<'db>(
319-
table: &InferenceTable<'db>,
412+
infcx: &InferCtxt<'db>,
413+
param_env: ParamEnv<'db>,
320414
ty: Ty<'db>,
321415
) -> Option<(Ty<'db>, PredicateObligations<'db>)> {
322-
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
323-
let Ok(normalized_ty) =
324-
ocx.structurally_normalize_ty(&ObligationCause::misc(), table.trait_env.env, ty)
416+
let mut ocx = ObligationCtxt::new(infcx);
417+
let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty)
325418
else {
326419
// We shouldn't have errors here in the old solver, except for
327420
// evaluate/fulfill mismatches, but that's not a reason for an ICE.
@@ -334,17 +427,3 @@ fn structurally_normalize_ty<'db>(
334427

335428
Some((normalized_ty, ocx.into_pending_obligations()))
336429
}
337-
338-
pub(crate) fn overloaded_deref_ty<'db>(
339-
table: &InferenceTable<'db>,
340-
ty: Ty<'db>,
341-
) -> Option<InferOk<'db, Ty<'db>>> {
342-
let interner = table.interner();
343-
344-
let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?;
345-
346-
let (normalized_ty, obligations) =
347-
structurally_normalize_ty(table, Ty::new_projection(interner, trait_target.into(), [ty]))?;
348-
349-
Some(InferOk { value: normalized_ty, obligations })
350-
}

0 commit comments

Comments
 (0)