Skip to content

Commit 0a912d0

Browse files
committed
Introduce term search to rust-analyzer
1 parent 3aa6306 commit 0a912d0

File tree

16 files changed

+2024
-66
lines changed

16 files changed

+2024
-66
lines changed

crates/hir-ty/src/infer.rs

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

7272
use cast::CastCheck;
7373
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>,
@@ -433,6 +464,18 @@ impl<'a> InferenceTable<'a> {
433464
true
434465
}
435466

467+
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
468+
pub(crate) fn unify_deeply<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
469+
let result = match self.try_unify(ty1, ty2) {
470+
Ok(r) => r,
471+
Err(_) => return false,
472+
};
473+
result.goals.iter().all(|goal| {
474+
let canonicalized = self.canonicalize(goal.clone());
475+
self.try_fulfill_obligation(&canonicalized)
476+
})
477+
}
478+
436479
/// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
437480
/// caller needs to deal with them.
438481
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
@@ -68,8 +68,8 @@ pub use builder::{ParamKind, TyBuilder};
6868
pub use chalk_ext::*;
6969
pub use infer::{
7070
closure::{CaptureKind, CapturedItem},
71-
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
72-
InferenceResult, OverloadedDeref, PointerCast,
71+
could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode,
72+
InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast,
7373
};
7474
pub use interner::Interner;
7575
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

@@ -33,11 +34,27 @@ pub struct MovedOutOfRef {
3334
pub span: MirSpan,
3435
}
3536

37+
#[derive(Debug, Clone, PartialEq, Eq)]
38+
pub struct PartiallyMoved {
39+
pub ty: Ty,
40+
pub span: MirSpan,
41+
pub local: LocalId,
42+
}
43+
44+
#[derive(Debug, Clone, PartialEq, Eq)]
45+
pub struct BorrowRegion {
46+
pub local: LocalId,
47+
pub kind: BorrowKind,
48+
pub places: Vec<MirSpan>,
49+
}
50+
3651
#[derive(Debug, Clone, PartialEq, Eq)]
3752
pub struct BorrowckResult {
3853
pub mir_body: Arc<MirBody>,
3954
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
4055
pub moved_out_of_ref: Vec<MovedOutOfRef>,
56+
pub partially_moved: Vec<PartiallyMoved>,
57+
pub borrow_regions: Vec<BorrowRegion>,
4158
}
4259

4360
fn all_mir_bodies(
@@ -77,6 +94,8 @@ pub fn borrowck_query(
7794
res.push(BorrowckResult {
7895
mutability_of_locals: mutability_of_locals(db, &body),
7996
moved_out_of_ref: moved_out_of_ref(db, &body),
97+
partially_moved: partially_moved(db, &body),
98+
borrow_regions: borrow_regions(db, &body),
8099
mir_body: body,
81100
});
82101
})?;
@@ -185,6 +204,149 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
185204
result
186205
}
187206

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

0 commit comments

Comments
 (0)