Skip to content

Commit a8606e5

Browse files
committed
Re-use the resolver in InferenceContext instead of rebuilding it on every expression change
1 parent e6ba791 commit a8606e5

File tree

7 files changed

+116
-38
lines changed

7 files changed

+116
-38
lines changed

crates/hir-def/src/body/scope.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl ExprScopes {
6666
self.scopes[scope].label.clone()
6767
}
6868

69+
/// Returns the scopes in ascending order.
6970
pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
7071
std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
7172
}

crates/hir-def/src/resolver.rs

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Name resolution façade.
2-
use std::{hash::BuildHasherDefault, sync::Arc};
2+
use std::{fmt, hash::BuildHasherDefault, sync::Arc};
33

44
use base_db::CrateId;
55
use hir_expand::name::{name, Name};
@@ -36,19 +36,34 @@ pub struct Resolver {
3636
module_scope: ModuleItemMap,
3737
}
3838

39-
#[derive(Debug, Clone)]
39+
#[derive(Clone)]
4040
struct ModuleItemMap {
4141
def_map: Arc<DefMap>,
4242
module_id: LocalModuleId,
4343
}
4444

45-
#[derive(Debug, Clone)]
45+
impl fmt::Debug for ModuleItemMap {
46+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47+
f.debug_struct("ModuleItemMap").field("module_id", &self.module_id).finish()
48+
}
49+
}
50+
51+
#[derive(Clone)]
4652
struct ExprScope {
4753
owner: DefWithBodyId,
4854
expr_scopes: Arc<ExprScopes>,
4955
scope_id: ScopeId,
5056
}
5157

58+
impl fmt::Debug for ExprScope {
59+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60+
f.debug_struct("ExprScope")
61+
.field("owner", &self.owner)
62+
.field("scope_id", &self.scope_id)
63+
.finish()
64+
}
65+
}
66+
5267
#[derive(Debug, Clone)]
5368
enum Scope {
5469
/// All the items and imported names of a module
@@ -478,8 +493,72 @@ impl Resolver {
478493
_ => None,
479494
})
480495
}
496+
/// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
497+
#[must_use]
498+
pub fn update_to_inner_scope(
499+
&mut self,
500+
db: &dyn DefDatabase,
501+
owner: DefWithBodyId,
502+
expr_id: ExprId,
503+
) -> UpdateGuard {
504+
#[inline(always)]
505+
fn append_expr_scope(
506+
db: &dyn DefDatabase,
507+
resolver: &mut Resolver,
508+
owner: DefWithBodyId,
509+
expr_scopes: &Arc<ExprScopes>,
510+
scope_id: ScopeId,
511+
) {
512+
resolver.scopes.push(Scope::ExprScope(ExprScope {
513+
owner,
514+
expr_scopes: expr_scopes.clone(),
515+
scope_id,
516+
}));
517+
if let Some(block) = expr_scopes.block(scope_id) {
518+
if let Some(def_map) = db.block_def_map(block) {
519+
let root = def_map.root();
520+
resolver
521+
.scopes
522+
.push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
523+
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
524+
// already traverses all parents, so this is O(n²). I think we could only store the
525+
// innermost module scope instead?
526+
}
527+
}
528+
}
529+
530+
let start = self.scopes.len();
531+
let innermost_scope = self.scopes().next();
532+
match innermost_scope {
533+
Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
534+
let expr_scopes = expr_scopes.clone();
535+
let scope_chain = expr_scopes
536+
.scope_chain(expr_scopes.scope_for(expr_id))
537+
.take_while(|&it| it != scope_id);
538+
for scope_id in scope_chain {
539+
append_expr_scope(db, self, owner, &expr_scopes, scope_id);
540+
}
541+
}
542+
_ => {
543+
let expr_scopes = db.expr_scopes(owner);
544+
let scope_chain = expr_scopes.scope_chain(expr_scopes.scope_for(expr_id));
545+
546+
for scope_id in scope_chain {
547+
append_expr_scope(db, self, owner, &expr_scopes, scope_id);
548+
}
549+
}
550+
}
551+
self.scopes[start..].reverse();
552+
UpdateGuard(start)
553+
}
554+
555+
pub fn reset_to_guard(&mut self, UpdateGuard(start): UpdateGuard) {
556+
self.scopes.truncate(start);
557+
}
481558
}
482559

560+
pub struct UpdateGuard(usize);
561+
483562
impl Resolver {
484563
fn scopes(&self) -> impl Iterator<Item = &Scope> {
485564
self.scopes.iter().rev()
@@ -576,19 +655,30 @@ impl Scope {
576655
}
577656
}
578657

579-
// needs arbitrary_self_types to be a method... or maybe move to the def?
580658
pub fn resolver_for_expr(db: &dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId) -> Resolver {
659+
let r = owner.resolver(db);
581660
let scopes = db.expr_scopes(owner);
582-
resolver_for_scope(db, owner, scopes.scope_for(expr_id))
661+
let scope_id = scopes.scope_for(expr_id);
662+
resolver_for_scope_(db, scopes, scope_id, r, owner)
583663
}
584664

585665
pub fn resolver_for_scope(
586666
db: &dyn DefDatabase,
587667
owner: DefWithBodyId,
588668
scope_id: Option<ScopeId>,
589669
) -> Resolver {
590-
let mut r = owner.resolver(db);
670+
let r = owner.resolver(db);
591671
let scopes = db.expr_scopes(owner);
672+
resolver_for_scope_(db, scopes, scope_id, r, owner)
673+
}
674+
675+
fn resolver_for_scope_(
676+
db: &dyn DefDatabase,
677+
scopes: Arc<ExprScopes>,
678+
scope_id: Option<ScopeId>,
679+
mut r: Resolver,
680+
owner: DefWithBodyId,
681+
) -> Resolver {
592682
let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
593683
r.scopes.reserve(scope_chain.len());
594684

crates/hir-ty/src/infer.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,6 @@ impl<'a> InferenceContext<'a> {
706706
}
707707

708708
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
709-
// FIXME use right resolver for block
710709
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
711710
let ty = ctx.lower_ty(type_ref);
712711
let ty = self.insert_type_vars(ty);
@@ -822,12 +821,11 @@ impl<'a> InferenceContext<'a> {
822821
Some(path) => path,
823822
None => return (self.err_ty(), None),
824823
};
825-
let resolver = &self.resolver;
826824
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
827825
// FIXME: this should resolve assoc items as well, see this example:
828826
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
829827
let (resolution, unresolved) = if value_ns {
830-
match resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
828+
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
831829
Some(ResolveValueResult::ValueNs(value)) => match value {
832830
ValueNs::EnumVariantId(var) => {
833831
let substs = ctx.substs_from_path(path, var.into(), true);
@@ -848,7 +846,7 @@ impl<'a> InferenceContext<'a> {
848846
None => return (self.err_ty(), None),
849847
}
850848
} else {
851-
match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
849+
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
852850
Some(it) => it,
853851
None => return (self.err_ty(), None),
854852
}

crates/hir-ty/src/infer/expr.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use hir_def::{
1515
generics::TypeOrConstParamData,
1616
lang_item::LangItem,
1717
path::{GenericArg, GenericArgs},
18-
resolver::resolver_for_expr,
1918
ConstParamId, FieldId, ItemContainerId, Lookup,
2019
};
2120
use hir_expand::name::{name, Name};
@@ -457,9 +456,10 @@ impl<'a> InferenceContext<'a> {
457456
}
458457
}
459458
Expr::Path(p) => {
460-
// FIXME this could be more efficient...
461-
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
462-
self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or_else(|| self.err_ty())
459+
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
460+
let ty = self.infer_path(p, tgt_expr.into()).unwrap_or_else(|| self.err_ty());
461+
self.resolver.reset_to_guard(g);
462+
ty
463463
}
464464
Expr::Continue { label } => {
465465
if let None = find_continuable(&mut self.breakables, label.as_ref()) {
@@ -1168,8 +1168,8 @@ impl<'a> InferenceContext<'a> {
11681168
expected: &Expectation,
11691169
) -> Ty {
11701170
let coerce_ty = expected.coercion_target_type(&mut self.table);
1171-
let old_resolver =
1172-
mem::replace(&mut self.resolver, resolver_for_expr(self.db.upcast(), self.owner, expr));
1171+
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
1172+
11731173
let (break_ty, ty) =
11741174
self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
11751175
for stmt in statements {
@@ -1256,7 +1256,7 @@ impl<'a> InferenceContext<'a> {
12561256
}
12571257
}
12581258
});
1259-
self.resolver = old_resolver;
1259+
self.resolver.reset_to_guard(g);
12601260

12611261
break_ty.unwrap_or(ty)
12621262
}

crates/hir-ty/src/infer/pat.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,8 @@ impl<'a> InferenceContext<'a> {
245245
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
246246
}
247247
Pat::Path(path) => {
248-
// FIXME use correct resolver for the surrounding expression
249-
let resolver = self.resolver.clone();
250-
self.infer_path(&resolver, path, pat.into()).unwrap_or_else(|| self.err_ty())
248+
// FIXME update resolver for the surrounding expression
249+
self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty())
251250
}
252251
Pat::Bind { mode, name: _, subpat } => {
253252
return self.infer_bind_pat(pat, *mode, default_bm, *subpat, &expected);

crates/hir-ty/src/infer/path.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use chalk_ir::cast::Cast;
44
use hir_def::{
55
path::{Path, PathSegment},
6-
resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs},
6+
resolver::{ResolveValueResult, TypeNs, ValueNs},
77
AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
88
};
99
use hir_expand::name::Name;
@@ -21,35 +21,25 @@ use crate::{
2121
use super::{ExprOrPatId, InferenceContext, TraitRef};
2222

2323
impl<'a> InferenceContext<'a> {
24-
pub(super) fn infer_path(
25-
&mut self,
26-
resolver: &Resolver,
27-
path: &Path,
28-
id: ExprOrPatId,
29-
) -> Option<Ty> {
30-
let ty = self.resolve_value_path(resolver, path, id)?;
24+
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
25+
let ty = self.resolve_value_path(path, id)?;
3126
let ty = self.insert_type_vars(ty);
3227
let ty = self.normalize_associated_types_in(ty);
3328
Some(ty)
3429
}
3530

36-
fn resolve_value_path(
37-
&mut self,
38-
resolver: &Resolver,
39-
path: &Path,
40-
id: ExprOrPatId,
41-
) -> Option<Ty> {
31+
fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
4232
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
4333
let Some(last) = path.segments().last() else { return None };
4434
let ty = self.make_ty(type_ref);
4535
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
46-
let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
36+
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
4737
let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
4838
self.resolve_ty_assoc_item(ty, last.name, id)?
4939
} else {
5040
// FIXME: report error, unresolved first path segment
5141
let value_or_partial =
52-
resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
42+
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
5343

5444
match value_or_partial {
5545
ResolveValueResult::ValueNs(it) => (it, None),

crates/hir-ty/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
163163
} else {
164164
ty.display_test(&db).to_string()
165165
};
166-
assert_eq!(actual, expected);
166+
assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
167167
}
168168
}
169169

@@ -179,7 +179,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
179179
} else {
180180
ty.display_test(&db).to_string()
181181
};
182-
assert_eq!(actual, expected);
182+
assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
183183
}
184184
if let Some(expected) = adjustments.remove(&range) {
185185
let adjustments = inference_result

0 commit comments

Comments
 (0)