Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/base-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ pub trait RootQueryDb: SourceDatabase + salsa::Database {
fn transitive_rev_deps(&self, of: Crate) -> FxHashSet<Crate>;
}

pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
// There is a bit of duplication here and in `CrateGraphBuilder` in the same method, but it's not terrible
// and removing that is a bit difficult.
let mut worklist = vec![crate_id];
Expand Down
14 changes: 14 additions & 0 deletions crates/hir-def/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,20 @@ impl<'db> Resolver<'db> {
self.item_scope_().0
}

#[inline]
pub fn top_level_def_map(&self) -> &'db DefMap {
self.module_scope.def_map
}

#[inline]
pub fn is_visible(&self, db: &dyn DefDatabase, visibility: Visibility) -> bool {
visibility.is_visible_from_def_map(
db,
self.module_scope.def_map,
self.module_scope.module_id,
)
}

pub fn generic_def(&self) -> Option<GenericDefId> {
self.scopes().find_map(|scope| match scope {
Scope::GenericParams { def, .. } => Some(*def),
Expand Down
221 changes: 150 additions & 71 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use triomphe::Arc;
use crate::{
TraitEnvironment,
db::HirDatabase,
infer::unify::InferenceTable,
infer::InferenceContext,
next_solver::{
Canonical, TraitRef, Ty, TyKind,
Canonical, DbInterner, ParamEnv, TraitRef, Ty, TyKind, TypingMode,
infer::{
InferOk,
DbInternerInferExt, InferCtxt,
traits::{Obligation, ObligationCause, PredicateObligations},
},
obligation_ctxt::ObligationCtxt,
Expand All @@ -38,14 +38,15 @@ pub fn autoderef<'db>(
env: Arc<TraitEnvironment<'db>>,
ty: Canonical<'db, Ty<'db>>,
) -> impl Iterator<Item = Ty<'db>> + use<'db> {
let mut table = InferenceTable::new(db, env, None);
let ty = table.instantiate_canonical(ty);
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let (ty, _) = infcx.instantiate_canonical(&ty);
let autoderef = Autoderef::new(&infcx, &env, ty);
let mut v = Vec::new();
while let Some((ty, _steps)) = autoderef.next() {
for (ty, _steps) in autoderef {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
// resolved, just replace with fallback type.
let resolved = autoderef.table.resolve_completely(ty);
let resolved = infcx.resolve_vars_if_possible(ty).replace_infer_with_error(interner);

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

// We use a trait here and a generic implementation unfortunately, because sometimes (specifically
// in place_op.rs), you need to have mutable access to the `InferenceContext` while the `Autoderef`
// borrows it.
pub(crate) trait AutoderefCtx<'db> {
fn infcx(&self) -> &InferCtxt<'db>;
fn env(&self) -> &TraitEnvironment<'db>;
}

pub(crate) struct DefaultAutoderefCtx<'a, 'db> {
infcx: &'a InferCtxt<'db>,
env: &'a TraitEnvironment<'db>,
}
impl<'db> AutoderefCtx<'db> for DefaultAutoderefCtx<'_, 'db> {
#[inline]
fn infcx(&self) -> &InferCtxt<'db> {
self.infcx
}
#[inline]
fn env(&self) -> &TraitEnvironment<'db> {
self.env
}
}

pub(crate) struct InferenceContextAutoderefCtx<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>);
impl<'db> AutoderefCtx<'db> for InferenceContextAutoderefCtx<'_, '_, 'db> {
#[inline]
fn infcx(&self) -> &InferCtxt<'db> {
&self.0.table.infer_ctxt
}
#[inline]
fn env(&self) -> &TraitEnvironment<'db> {
&self.0.table.trait_env
}
}

/// Recursively dereference a type, considering both built-in
/// dereferences (`*`) and the `Deref` trait.
/// Although called `Autoderef` it can be configured to use the
/// `Receiver` trait instead of the `Deref` trait.
pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
// Meta infos:
pub(crate) table: &'a mut InferenceTable<'db>,
ctx: Ctx,
traits: Option<AutoderefTraits>,

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

impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, Steps> {
pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
GeneralAutoderef<'db, DefaultAutoderefCtx<'a, 'db>, Steps>;
pub(crate) type InferenceContextAutoderef<'a, 'b, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
GeneralAutoderef<'db, InferenceContextAutoderefCtx<'a, 'b, 'db>, Steps>;

impl<'db, Ctx, Steps> Iterator for GeneralAutoderef<'db, Ctx, Steps>
where
Ctx: AutoderefCtx<'db>,
Steps: TrackAutoderefSteps<'db>,
{
type Item = (Ty<'db>, usize);

fn next(&mut self) -> Option<Self::Item> {
Expand All @@ -148,26 +193,26 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, S
// be better to skip this clause and use the Overloaded case only, since &T
// and &mut T implement Receiver. But built-in derefs apply equally to Receiver
// and Deref, and this has benefits for const and the emitted MIR.
let (kind, new_ty) = if let Some(ty) =
self.state.cur_ty.builtin_deref(self.table.db, self.include_raw_pointers)
{
debug_assert_eq!(ty, self.table.infer_ctxt.resolve_vars_if_possible(ty));
// NOTE: we may still need to normalize the built-in deref in case
// we have some type like `&<Ty as Trait>::Assoc`, since users of
// autoderef expect this type to have been structurally normalized.
if let TyKind::Alias(..) = ty.kind() {
let (normalized_ty, obligations) = structurally_normalize_ty(self.table, ty)?;
self.state.obligations.extend(obligations);
(AutoderefKind::Builtin, normalized_ty)
let (kind, new_ty) =
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
debug_assert_eq!(ty, self.infcx().resolve_vars_if_possible(ty));
// NOTE: we may still need to normalize the built-in deref in case
// we have some type like `&<Ty as Trait>::Assoc`, since users of
// autoderef expect this type to have been structurally normalized.
if let TyKind::Alias(..) = ty.kind() {
let (normalized_ty, obligations) =
structurally_normalize_ty(self.infcx(), self.env().env, ty)?;
self.state.obligations.extend(obligations);
(AutoderefKind::Builtin, normalized_ty)
} else {
(AutoderefKind::Builtin, ty)
}
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
// The overloaded deref check already normalizes the pointee type.
(AutoderefKind::Overloaded, ty)
} else {
(AutoderefKind::Builtin, ty)
}
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
// The overloaded deref check already normalizes the pointee type.
(AutoderefKind::Overloaded, ty)
} else {
return None;
};
return None;
};

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

impl<'a, 'db> Autoderef<'a, 'db> {
pub(crate) fn new(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
Self::new_impl(table, base_ty)
#[inline]
pub(crate) fn new_with_tracking(
infcx: &'a InferCtxt<'db>,
env: &'a TraitEnvironment<'db>,
base_ty: Ty<'db>,
) -> Self {
Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty)
}
}

impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> {
#[inline]
pub(crate) fn new_from_inference_context(
ctx: &'a mut InferenceContext<'b, 'db>,
base_ty: Ty<'db>,
) -> Self {
Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty)
}

#[inline]
pub(crate) fn ctx(&mut self) -> &mut InferenceContext<'b, 'db> {
self.ctx.0
}
}

impl<'a, 'db> Autoderef<'a, 'db, usize> {
pub(crate) fn new_no_tracking(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
Self::new_impl(table, base_ty)
#[inline]
pub(crate) fn new(
infcx: &'a InferCtxt<'db>,
env: &'a TraitEnvironment<'db>,
base_ty: Ty<'db>,
) -> Self {
Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty)
}
}

impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
fn new_impl(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
Autoderef {
impl<'db, Ctx, Steps> GeneralAutoderef<'db, Ctx, Steps>
where
Ctx: AutoderefCtx<'db>,
Steps: TrackAutoderefSteps<'db>,
{
#[inline]
fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self {
GeneralAutoderef {
state: AutoderefSnapshot {
steps: Steps::default(),
cur_ty: table.infer_ctxt.resolve_vars_if_possible(base_ty),
cur_ty: ctx.infcx().resolve_vars_if_possible(base_ty),
obligations: PredicateObligations::new(),
at_start: true,
reached_recursion_limit: false,
},
table,
ctx,
traits: None,
include_raw_pointers: false,
use_receiver_trait: false,
}
}

#[inline]
fn infcx(&self) -> &InferCtxt<'db> {
self.ctx.infcx()
}

#[inline]
fn env(&self) -> &TraitEnvironment<'db> {
self.ctx.env()
}

#[inline]
fn interner(&self) -> DbInterner<'db> {
self.infcx().interner
}

#[inline]
fn db(&self) -> &'db dyn HirDatabase {
self.interner().db
}

fn autoderef_traits(&mut self) -> Option<AutoderefTraits> {
match &mut self.traits {
Some(it) => Some(*it),
Expand All @@ -219,25 +314,23 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
(|| {
Some(AutoderefTraits {
trait_: LangItem::Receiver
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
.resolve_trait(self.db(), self.env().krate)?,
trait_target: LangItem::ReceiverTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
.resolve_type_alias(self.db(), self.env().krate)?,
})
})()
.or_else(|| {
Some(AutoderefTraits {
trait_: LangItem::Deref
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?,
trait_target: LangItem::DerefTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
.resolve_type_alias(self.db(), self.env().krate)?,
})
})?
} else {
AutoderefTraits {
trait_: LangItem::Deref
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?,
trait_target: LangItem::DerefTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
.resolve_type_alias(self.db(), self.env().krate)?,
}
};
Some(*self.traits.insert(traits))
Expand All @@ -247,31 +340,32 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {

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

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

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

let (normalized_ty, obligations) = structurally_normalize_ty(
self.table,
self.infcx(),
self.env().env,
Ty::new_projection(interner, trait_target.into(), [ty]),
)?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);

Some(self.table.infer_ctxt.resolve_vars_if_possible(normalized_ty))
Some(self.infcx().resolve_vars_if_possible(normalized_ty))
}

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

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

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

Some((normalized_ty, ocx.into_pending_obligations()))
}

pub(crate) fn overloaded_deref_ty<'db>(
table: &InferenceTable<'db>,
ty: Ty<'db>,
) -> Option<InferOk<'db, Ty<'db>>> {
let interner = table.interner();

let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?;

let (normalized_ty, obligations) =
structurally_normalize_ty(table, Ty::new_projection(interner, trait_target.into(), [ty]))?;

Some(InferOk { value: normalized_ty, obligations })
}
Loading
Loading