Skip to content

internal: Don't allocate autoderef steps when not needed #17961

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 1 commit into from
Aug 25, 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
7 changes: 4 additions & 3 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
# prettier format
f247090558c9ba3c551566eae5882b7ca865225f

# subtree syncs
932d85b52946d917deab2c23ead552f7f713b828
# pre-josh subtree syncs
3e358a6827d83e8d6473913a5e304734aadfed04
932d85b52946d917deab2c23ead552f7f713b828
9d2cb42a413e51deb50b36794a2e1605381878fc
f532576ac53ddcc666bc8d59e0b6437065e2f599
b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15
c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1
f532576ac53ddcc666bc8d59e0b6437065e2f599
73 changes: 53 additions & 20 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! reference to a type with the field `bar`. This is an approximation of the
//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).

use std::mem;

use chalk_ir::cast::Cast;
use hir_def::lang_item::LangItem;
use hir_expand::name::Name;
Expand Down Expand Up @@ -37,7 +39,7 @@ pub fn autoderef(
) -> impl Iterator<Item = Ty> {
let mut table = InferenceTable::new(db, env);
let ty = table.instantiate_canonical(ty);
let mut autoderef = Autoderef::new(&mut table, ty, false);
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false);
let mut v = Vec::new();
while let Some((ty, _steps)) = autoderef.next() {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
Expand All @@ -58,41 +60,76 @@ pub fn autoderef(
v.into_iter()
}

trait TrackAutoderefSteps {
fn len(&self) -> usize;
fn push(&mut self, kind: AutoderefKind, ty: &Ty);
}

impl TrackAutoderefSteps for usize {
fn len(&self) -> usize {
*self
}
fn push(&mut self, _: AutoderefKind, _: &Ty) {
*self += 1;
}
}
impl TrackAutoderefSteps for Vec<(AutoderefKind, Ty)> {
fn len(&self) -> usize {
self.len()
}
fn push(&mut self, kind: AutoderefKind, ty: &Ty) {
self.push((kind, ty.clone()));
}
}

#[derive(Debug)]
pub(crate) struct Autoderef<'a, 'db> {
pub(crate) table: &'a mut InferenceTable<'db>,
pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
pub(crate) table: &'table mut InferenceTable<'db>,
ty: Ty,
at_start: bool,
steps: Vec<(AutoderefKind, Ty)>,
steps: T,
explicit: bool,
}

impl<'a, 'db> Autoderef<'a, 'db> {
pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
impl<'table, 'db> Autoderef<'table, 'db> {
pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
let ty = table.resolve_ty_shallow(&ty);
Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
}

pub(crate) fn step_count(&self) -> usize {
self.steps.len()
}

pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
&self.steps
}
}

impl<'table, 'db> Autoderef<'table, 'db, usize> {
pub(crate) fn new_no_tracking(
table: &'table mut InferenceTable<'db>,
ty: Ty,
explicit: bool,
) -> Self {
let ty = table.resolve_ty_shallow(&ty);
Autoderef { table, ty, at_start: true, steps: 0, explicit }
}
}

#[allow(private_bounds)]
impl<'table, 'db, T: TrackAutoderefSteps> Autoderef<'table, 'db, T> {
pub(crate) fn step_count(&self) -> usize {
self.steps.len()
}

pub(crate) fn final_ty(&self) -> Ty {
self.ty.clone()
}
}

impl Iterator for Autoderef<'_, '_> {
impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
type Item = (Ty, usize);

#[tracing::instrument(skip_all)]
fn next(&mut self) -> Option<Self::Item> {
if self.at_start {
self.at_start = false;
if mem::take(&mut self.at_start) {
return Some((self.ty.clone(), 0));
}

Expand All @@ -102,7 +139,7 @@ impl Iterator for Autoderef<'_, '_> {

let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;

self.steps.push((kind, self.ty.clone()));
self.steps.push(kind, &self.ty);
self.ty = new_ty;

Some((self.ty.clone(), self.step_count()))
Expand All @@ -129,12 +166,8 @@ pub(crate) fn builtin_deref<'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(db, adt) {
substs.at(Interner, 0).ty(Interner)
} else {
None
}
&TyKind::Adt(chalk_ir::AdtId(adt), ref substs) if crate::lang_items::is_box(db, adt) => {
substs.at(Interner, 0).ty(Interner)
}
_ => None,
}
Expand Down
6 changes: 3 additions & 3 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,7 @@ fn iterate_method_candidates_by_receiver(
// be found in any of the derefs of receiver_ty, so we have to go through
// that, including raw derefs.
table.run_in_snapshot(|table| {
let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
while let Some((self_ty, _)) = autoderef.next() {
iterate_inherent_methods(
&self_ty,
Expand All @@ -1082,7 +1082,7 @@ fn iterate_method_candidates_by_receiver(
ControlFlow::Continue(())
})?;
table.run_in_snapshot(|table| {
let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
while let Some((self_ty, _)) = autoderef.next() {
if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) {
// don't try to resolve methods on unknown types
Expand Down Expand Up @@ -1657,7 +1657,7 @@ fn autoderef_method_receiver(
ty: Ty,
) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
let mut deref_chain: Vec<_> = Vec::new();
let mut autoderef = autoderef::Autoderef::new(table, ty, false);
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false);
while let Some((ty, derefs)) = autoderef.next() {
deref_chain.push((
autoderef.table.canonicalize(ty),
Expand Down