Skip to content

internal: Some method resolution cleanups #16749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ anyhow = "1.0.75"
arrayvec = "0.7.4"
bitflags = "2.4.1"
cargo_metadata = "0.18.1"
chalk-solve = { version = "0.96.0", default-features = false }
chalk-ir = "0.96.0"
chalk-recursive = { version = "0.96.0", default-features = false }
chalk-derive = "0.96.0"
command-group = "2.0.1"
crossbeam-channel = "0.5.8"
dissimilar = "1.0.7"
Expand Down
8 changes: 4 additions & 4 deletions crates/hir-ty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ oorandom = "11.1.3"
tracing.workspace = true
rustc-hash.workspace = true
scoped-tls = "1.0.0"
chalk-solve = { version = "0.96.0", default-features = false }
chalk-ir = "0.96.0"
chalk-recursive = { version = "0.96.0", default-features = false }
chalk-derive = "0.96.0"
chalk-solve.workspace = true
chalk-ir.workspace = true
chalk-recursive.workspace = true
chalk-derive.workspace = true
la-arena.workspace = true
once_cell = "1.17.0"
triomphe.workspace = true
Expand Down
6 changes: 3 additions & 3 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,23 +113,23 @@ pub(crate) fn autoderef_step(
ty: Ty,
explicit: bool,
) -> Option<(AutoderefKind, Ty)> {
if let Some(derefed) = builtin_deref(table, &ty, explicit) {
if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else {
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
}
}

pub(crate) fn builtin_deref<'ty>(
table: &mut InferenceTable<'_>,
db: &dyn HirDatabase,
ty: &'ty Ty,
explicit: bool,
) -> Option<&'ty Ty> {
match ty.kind(Interner) {
TyKind::Ref(.., ty) => Some(ty),
TyKind::Raw(.., ty) if explicit => Some(ty),
&TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
if crate::lang_items::is_box(table.db, adt) {
if crate::lang_items::is_box(db, adt) {
substs.at(Interner, 0).ty(Interner)
} else {
None
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/coerce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ impl InferenceTable<'_> {
let goal: InEnvironment<DomainGoal> =
InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner));

let canonicalized = self.canonicalize(goal);
let canonicalized = self.canonicalize_with_free_vars(goal);

// FIXME: rustc's coerce_unsized is more specialized -- it only tries to
// solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
Expand Down
24 changes: 11 additions & 13 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,13 @@ impl InferenceContext<'_> {
Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none());
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
let (res, derefed_callee) = 'b: {
// manual loop to be able to access `derefs.table`
while let Some((callee_deref_ty, _)) = derefs.next() {
let res = derefs.table.callable_sig(&callee_deref_ty, args.len());
if res.is_some() {
break 'b (res, callee_deref_ty);
}
let (res, derefed_callee) = loop {
let Some((callee_deref_ty, _)) = derefs.next() else {
break (None, callee_ty.clone());
};
if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
break (Some(res), callee_deref_ty);
}
(None, callee_ty.clone())
};
// if the function is unresolved, we use is_varargs=true to
// suppress the arg count diagnostic here
Expand Down Expand Up @@ -657,7 +655,7 @@ impl InferenceContext<'_> {
);
}
}
if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) {
if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
self.resolve_ty_shallow(derefed)
} else {
deref_by_trait(&mut self.table, inner_ty)
Expand Down Expand Up @@ -774,7 +772,7 @@ impl InferenceContext<'_> {
let receiver_adjustments = method_resolution::resolve_indexing_op(
self.db,
self.table.trait_env.clone(),
canonicalized.value,
canonicalized,
index_trait,
);
let (self_ty, mut adj) = receiver_adjustments
Expand Down Expand Up @@ -1559,7 +1557,7 @@ impl InferenceContext<'_> {
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
let resolved = method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
&canonicalized_receiver,
self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
Expand Down Expand Up @@ -1608,7 +1606,7 @@ impl InferenceContext<'_> {

let resolved = method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
&canonicalized_receiver,
self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
Expand Down Expand Up @@ -1641,7 +1639,7 @@ impl InferenceContext<'_> {
};

let assoc_func_with_same_name = method_resolution::iterate_method_candidates(
&canonicalized_receiver.value,
&canonicalized_receiver,
self.db,
self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl InferenceContext<'_> {

let mut not_visible = None;
let res = method_resolution::iterate_method_candidates(
&canonical_ty.value,
&canonical_ty,
self.db,
self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
Expand Down
75 changes: 36 additions & 39 deletions crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@ use crate::{
};

impl InferenceContext<'_> {
pub(super) fn canonicalize<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
&mut self,
t: T,
) -> Canonicalized<T>
pub(super) fn canonicalize<T>(&mut self, t: T) -> Canonical<T>
where
T: HasInterner<Interner = Interner>,
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
{
self.table.canonicalize(t)
}
Expand Down Expand Up @@ -128,14 +125,14 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
}),
);
for (i, v) in solution.value.iter(Interner).enumerate() {
let var = self.free_vars[i].clone();
let var = &self.free_vars[i];
if let Some(ty) = v.ty(Interner) {
// eagerly replace projections in the type; we may be getting types
// e.g. from where clauses where this hasn't happened yet
let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner));
ctx.unify(var.assert_ty_ref(Interner), &ty);
} else {
let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), Interner));
let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner));
}
}
}
Expand Down Expand Up @@ -243,7 +240,7 @@ pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>,
var_unification_table: ChalkInferenceTable,
type_variable_table: Vec<TypeVariableFlags>,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
/// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on
/// temporary allocations.
Expand All @@ -252,8 +249,8 @@ pub(crate) struct InferenceTable<'a> {

pub(crate) struct InferenceTableSnapshot {
var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
type_variable_table_snapshot: Vec<TypeVariableFlags>,
}

impl<'a> InferenceTable<'a> {
Expand All @@ -262,7 +259,7 @@ impl<'a> InferenceTable<'a> {
db,
trait_env,
var_unification_table: ChalkInferenceTable::new(),
type_variable_table: Vec::new(),
type_variable_table: SmallVec::new(),
pending_obligations: Vec::new(),
resolve_obligations_buffer: Vec::new(),
}
Expand Down Expand Up @@ -292,27 +289,24 @@ impl<'a> InferenceTable<'a> {
}

fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
let is_diverging = self
.type_variable_table
.get(iv.index() as usize)
.map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING));
if is_diverging {
return TyKind::Never.intern(Interner);
}
match kind {
_ if self
.type_variable_table
.get(iv.index() as usize)
.map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) =>
{
TyKind::Never
}
TyVariableKind::General => TyKind::Error,
TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)),
TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)),
}
.intern(Interner)
}

pub(crate) fn canonicalize<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
&mut self,
t: T,
) -> Canonicalized<T>
pub(crate) fn canonicalize_with_free_vars<T>(&mut self, t: T) -> Canonicalized<T>
where
T: HasInterner<Interner = Interner>,
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
{
// try to resolve obligations before canonicalizing, since this might
// result in new knowledge about variables
Expand All @@ -326,6 +320,16 @@ impl<'a> InferenceTable<'a> {
Canonicalized { value: result.quantified, free_vars }
}

pub(crate) fn canonicalize<T>(&mut self, t: T) -> Canonical<T>
where
T: TypeFoldable<Interner> + HasInterner<Interner = Interner>,
{
// try to resolve obligations before canonicalizing, since this might
// result in new knowledge about variables
self.resolve_obligations_as_possible();
self.var_unification_table.canonicalize(Interner, t).quantified
}

/// Recurses through the given type, normalizing associated types mentioned
/// in it by replacing them by type variables and registering obligations to
/// resolve later. This should be done once for every type we get from some
Expand Down Expand Up @@ -434,6 +438,7 @@ impl<'a> InferenceTable<'a> {
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
// TODO check this vec here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what sort of check would happen here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that was a note to myself to check if we can cache the allocation for that vec here

self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
}

Expand Down Expand Up @@ -541,7 +546,7 @@ impl<'a> InferenceTable<'a> {
Err(_) => return false,
};
result.goals.iter().all(|goal| {
let canonicalized = self.canonicalize(goal.clone());
let canonicalized = self.canonicalize_with_free_vars(goal.clone());
self.try_resolve_obligation(&canonicalized).is_some()
})
}
Expand Down Expand Up @@ -575,19 +580,15 @@ impl<'a> InferenceTable<'a> {

pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot {
let var_table_snapshot = self.var_unification_table.snapshot();
let type_variable_table_snapshot = self.type_variable_table.clone();
let type_variable_table = self.type_variable_table.clone();
let pending_obligations = self.pending_obligations.clone();
InferenceTableSnapshot {
var_table_snapshot,
pending_obligations,
type_variable_table_snapshot,
}
InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table }
}

#[tracing::instrument(skip_all)]
pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
self.type_variable_table = snapshot.type_variable_table_snapshot;
self.type_variable_table = snapshot.type_variable_table;
self.pending_obligations = snapshot.pending_obligations;
}

Expand All @@ -606,7 +607,7 @@ impl<'a> InferenceTable<'a> {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
let canonicalized = self.canonicalize(in_env);

self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value)
self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized)
}

pub(crate) fn register_obligation(&mut self, goal: Goal) {
Expand All @@ -615,7 +616,7 @@ impl<'a> InferenceTable<'a> {
}

fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
let canonicalized = self.canonicalize(goal);
let canonicalized = self.canonicalize_with_free_vars(goal);
let solution = self.try_resolve_obligation(&canonicalized);
if matches!(solution, Some(Solution::Ambig(_))) {
self.pending_obligations.push(canonicalized);
Expand Down Expand Up @@ -798,7 +799,7 @@ impl<'a> InferenceTable<'a> {
let trait_data = self.db.trait_data(fn_once_trait);
let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?;

let mut arg_tys = vec![];
let mut arg_tys = Vec::with_capacity(num_args);
let arg_ty = TyBuilder::tuple(num_args)
.fill(|it| {
let arg = match it {
Expand Down Expand Up @@ -828,11 +829,7 @@ impl<'a> InferenceTable<'a> {
environment: trait_env.clone(),
};
let canonical = self.canonicalize(obligation.clone());
if self
.db
.trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
.is_some()
{
if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() {
self.register_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection);
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
Expand All @@ -845,7 +842,7 @@ impl<'a> InferenceTable<'a> {
let canonical = self.canonicalize(obligation.clone());
if self
.db
.trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
.trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
.is_some()
{
return Some((fn_x, arg_tys, return_ty));
Expand Down
Loading