Skip to content

Commit bb3c7cf

Browse files
committed
Introduce term search to rust-analyzer
1 parent ddf105b commit bb3c7cf

File tree

15 files changed

+2022
-66
lines changed

15 files changed

+2022
-66
lines changed

crates/hir-ty/src/infer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ use crate::{
6868
#[allow(unreachable_pub)]
6969
pub use coerce::could_coerce;
7070
#[allow(unreachable_pub)]
71-
pub use unify::could_unify;
71+
pub use unify::{could_unify, could_unify_deeply};
7272

7373
use cast::CastCheck;
7474
pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,37 @@ pub fn could_unify(
8282
unify(db, env, tys).is_some()
8383
}
8484

85+
pub fn could_unify_deeply(
86+
db: &dyn HirDatabase,
87+
env: Arc<TraitEnvironment>,
88+
tys: &Canonical<(Ty, Ty)>,
89+
) -> bool {
90+
let mut table = InferenceTable::new(db, env);
91+
let vars = Substitution::from_iter(
92+
Interner,
93+
tys.binders.iter(Interner).map(|it| match &it.kind {
94+
chalk_ir::VariableKind::Ty(_) => {
95+
GenericArgData::Ty(table.new_type_var()).intern(Interner)
96+
}
97+
chalk_ir::VariableKind::Lifetime => {
98+
GenericArgData::Ty(table.new_type_var()).intern(Interner)
99+
} // FIXME: maybe wrong?
100+
chalk_ir::VariableKind::Const(ty) => {
101+
GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
102+
}
103+
}),
104+
);
105+
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
106+
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
107+
let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars);
108+
let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars);
109+
// table.resolve_obligations_as_possible();
110+
// table.propagate_diverging_flag();
111+
// let ty1_with_vars = table.resolve_completely(ty1_with_vars);
112+
// let ty2_with_vars = table.resolve_completely(ty2_with_vars);
113+
table.unify_deeply(&ty1_with_vars, &ty2_with_vars)
114+
}
115+
85116
pub(crate) fn unify(
86117
db: &dyn HirDatabase,
87118
env: Arc<TraitEnvironment>,
@@ -431,6 +462,18 @@ impl<'a> InferenceTable<'a> {
431462
true
432463
}
433464

465+
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
466+
pub(crate) fn unify_deeply<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
467+
let result = match self.try_unify(ty1, ty2) {
468+
Ok(r) => r,
469+
Err(_) => return false,
470+
};
471+
result.goals.iter().all(|goal| {
472+
let canonicalized = self.canonicalize(goal.clone());
473+
self.try_fulfill_obligation(&canonicalized)
474+
})
475+
}
476+
434477
/// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
435478
/// caller needs to deal with them.
436479
pub(crate) fn try_unify<T: ?Sized + Zip<Interner>>(
@@ -661,6 +704,38 @@ impl<'a> InferenceTable<'a> {
661704
}
662705
}
663706

707+
fn try_fulfill_obligation(
708+
&mut self,
709+
canonicalized: &Canonicalized<InEnvironment<Goal>>,
710+
) -> bool {
711+
let solution = self.db.trait_solve(
712+
self.trait_env.krate,
713+
self.trait_env.block,
714+
canonicalized.value.clone(),
715+
);
716+
717+
// FIXME: Does just returning `solution.is_some()` work?
718+
match solution {
719+
Some(Solution::Unique(canonical_subst)) => {
720+
canonicalized.apply_solution(
721+
self,
722+
Canonical {
723+
binders: canonical_subst.binders,
724+
// FIXME: handle constraints
725+
value: canonical_subst.value.subst,
726+
},
727+
);
728+
true
729+
}
730+
Some(Solution::Ambig(Guidance::Definite(substs))) => {
731+
canonicalized.apply_solution(self, substs);
732+
true
733+
}
734+
Some(_) => true,
735+
None => false,
736+
}
737+
}
738+
664739
pub(crate) fn callable_sig(
665740
&mut self,
666741
ty: &Ty,

crates/hir-ty/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ pub use builder::{ParamKind, TyBuilder};
7979
pub use chalk_ext::*;
8080
pub use infer::{
8181
closure::{CaptureKind, CapturedItem},
82-
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
83-
InferenceResult, OverloadedDeref, PointerCast,
82+
could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode,
83+
InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast,
8484
};
8585
pub use interner::Interner;
8686
pub use lower::{

crates/hir-ty/src/mir/borrowck.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::iter;
77

88
use hir_def::{DefWithBodyId, HasModule};
99
use la_arena::ArenaMap;
10+
use rustc_hash::FxHashMap;
1011
use stdx::never;
1112
use triomphe::Arc;
1213

@@ -36,11 +37,27 @@ pub struct MovedOutOfRef {
3637
pub span: MirSpan,
3738
}
3839

40+
#[derive(Debug, Clone, PartialEq, Eq)]
41+
pub struct PartiallyMoved {
42+
pub ty: Ty,
43+
pub span: MirSpan,
44+
pub local: LocalId,
45+
}
46+
47+
#[derive(Debug, Clone, PartialEq, Eq)]
48+
pub struct BorrowRegion {
49+
pub local: LocalId,
50+
pub kind: BorrowKind,
51+
pub places: Vec<MirSpan>,
52+
}
53+
3954
#[derive(Debug, Clone, PartialEq, Eq)]
4055
pub struct BorrowckResult {
4156
pub mir_body: Arc<MirBody>,
4257
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
4358
pub moved_out_of_ref: Vec<MovedOutOfRef>,
59+
pub partially_moved: Vec<PartiallyMoved>,
60+
pub borrow_regions: Vec<BorrowRegion>,
4461
}
4562

4663
fn all_mir_bodies(
@@ -80,6 +97,8 @@ pub fn borrowck_query(
8097
res.push(BorrowckResult {
8198
mutability_of_locals: mutability_of_locals(db, &body),
8299
moved_out_of_ref: moved_out_of_ref(db, &body),
100+
partially_moved: partially_moved(db, &body),
101+
borrow_regions: borrow_regions(db, &body),
83102
mir_body: body,
84103
});
85104
})?;
@@ -188,6 +207,149 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
188207
result
189208
}
190209

210+
fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved> {
211+
let mut result = vec![];
212+
let mut for_operand = |op: &Operand, span: MirSpan| match op {
213+
Operand::Copy(p) | Operand::Move(p) => {
214+
let mut ty: Ty = body.locals[p.local].ty.clone();
215+
for proj in p.projection.lookup(&body.projection_store) {
216+
ty = proj.projected_ty(
217+
ty,
218+
db,
219+
|c, subst, f| {
220+
let (def, _) = db.lookup_intern_closure(c.into());
221+
let infer = db.infer(def);
222+
let (captures, _) = infer.closure_info(&c);
223+
let parent_subst = ClosureSubst(subst).parent_subst();
224+
captures
225+
.get(f)
226+
.expect("broken closure field")
227+
.ty
228+
.clone()
229+
.substitute(Interner, parent_subst)
230+
},
231+
body.owner.module(db.upcast()).krate(),
232+
);
233+
}
234+
if !ty.clone().is_copy(db, body.owner)
235+
&& !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR)
236+
{
237+
result.push(PartiallyMoved { span, ty, local: p.local });
238+
}
239+
}
240+
Operand::Constant(_) | Operand::Static(_) => (),
241+
};
242+
for (_, block) in body.basic_blocks.iter() {
243+
db.unwind_if_cancelled();
244+
for statement in &block.statements {
245+
match &statement.kind {
246+
StatementKind::Assign(_, r) => match r {
247+
Rvalue::ShallowInitBoxWithAlloc(_) => (),
248+
Rvalue::ShallowInitBox(o, _)
249+
| Rvalue::UnaryOp(_, o)
250+
| Rvalue::Cast(_, o, _)
251+
| Rvalue::Repeat(o, _)
252+
| Rvalue::Use(o) => for_operand(o, statement.span),
253+
Rvalue::CopyForDeref(_)
254+
| Rvalue::Discriminant(_)
255+
| Rvalue::Len(_)
256+
| Rvalue::Ref(_, _) => (),
257+
Rvalue::CheckedBinaryOp(_, o1, o2) => {
258+
for_operand(o1, statement.span);
259+
for_operand(o2, statement.span);
260+
}
261+
Rvalue::Aggregate(_, ops) => {
262+
for op in ops.iter() {
263+
for_operand(op, statement.span);
264+
}
265+
}
266+
},
267+
StatementKind::FakeRead(_)
268+
| StatementKind::Deinit(_)
269+
| StatementKind::StorageLive(_)
270+
| StatementKind::StorageDead(_)
271+
| StatementKind::Nop => (),
272+
}
273+
}
274+
match &block.terminator {
275+
Some(terminator) => match &terminator.kind {
276+
TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
277+
TerminatorKind::FalseEdge { .. }
278+
| TerminatorKind::FalseUnwind { .. }
279+
| TerminatorKind::Goto { .. }
280+
| TerminatorKind::UnwindResume
281+
| TerminatorKind::CoroutineDrop
282+
| TerminatorKind::Abort
283+
| TerminatorKind::Return
284+
| TerminatorKind::Unreachable
285+
| TerminatorKind::Drop { .. } => (),
286+
TerminatorKind::DropAndReplace { value, .. } => {
287+
for_operand(value, terminator.span);
288+
}
289+
TerminatorKind::Call { func, args, .. } => {
290+
for_operand(func, terminator.span);
291+
args.iter().for_each(|it| for_operand(it, terminator.span));
292+
}
293+
TerminatorKind::Assert { cond, .. } => {
294+
for_operand(cond, terminator.span);
295+
}
296+
TerminatorKind::Yield { value, .. } => {
297+
for_operand(value, terminator.span);
298+
}
299+
},
300+
None => (),
301+
}
302+
}
303+
result.shrink_to_fit();
304+
result
305+
}
306+
307+
fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec<BorrowRegion> {
308+
let mut borrows = FxHashMap::default();
309+
for (_, block) in body.basic_blocks.iter() {
310+
db.unwind_if_cancelled();
311+
for statement in &block.statements {
312+
match &statement.kind {
313+
StatementKind::Assign(_, r) => match r {
314+
Rvalue::Ref(kind, p) => {
315+
borrows
316+
.entry(p.local)
317+
.and_modify(|it: &mut BorrowRegion| {
318+
it.places.push(statement.span);
319+
})
320+
.or_insert_with(|| BorrowRegion {
321+
local: p.local,
322+
kind: *kind,
323+
places: vec![statement.span],
324+
});
325+
}
326+
_ => (),
327+
},
328+
_ => (),
329+
}
330+
}
331+
match &block.terminator {
332+
Some(terminator) => match &terminator.kind {
333+
TerminatorKind::FalseEdge { .. }
334+
| TerminatorKind::FalseUnwind { .. }
335+
| TerminatorKind::Goto { .. }
336+
| TerminatorKind::UnwindResume
337+
| TerminatorKind::CoroutineDrop
338+
| TerminatorKind::Abort
339+
| TerminatorKind::Return
340+
| TerminatorKind::Unreachable
341+
| TerminatorKind::Drop { .. } => (),
342+
TerminatorKind::DropAndReplace { .. } => {}
343+
TerminatorKind::Call { .. } => {}
344+
_ => (),
345+
},
346+
None => (),
347+
}
348+
}
349+
350+
borrows.into_values().collect()
351+
}
352+
191353
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192354
enum ProjectionCase {
193355
/// Projection is a local

crates/hir-ty/src/mir/lower.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
12461246
self.push_assignment(current, place, op.into(), expr_id.into());
12471247
Ok(Some(current))
12481248
}
1249-
Expr::Underscore => not_supported!("underscore"),
1249+
Expr::Underscore => Ok(Some(current)),
12501250
}
12511251
}
12521252

0 commit comments

Comments
 (0)