Skip to content

Commit

Permalink
Initial support for implicit drop inlay hint
Browse files Browse the repository at this point in the history
  • Loading branch information
HKalbasi committed Dec 1, 2023
1 parent 56abc0a commit 7ef2be2
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 25 deletions.
14 changes: 14 additions & 0 deletions crates/hir-ty/src/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ impl ProjectionStore {
impl ProjectionId {
pub const EMPTY: ProjectionId = ProjectionId(0);

pub fn is_empty(self) -> bool {
self == ProjectionId::EMPTY
}

pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
store.id_to_proj.get(&self).unwrap()
}
Expand Down Expand Up @@ -1069,6 +1073,10 @@ pub struct MirBody {
}

impl MirBody {
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
}

fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
fn for_operand(
op: &mut Operand,
Expand Down Expand Up @@ -1188,3 +1196,9 @@ pub enum MirSpan {
}

impl_from!(ExprId, PatId for MirSpan);

impl From<&ExprId> for MirSpan {
fn from(value: &ExprId) -> Self {
(*value).into()
}
}
54 changes: 34 additions & 20 deletions crates/hir-ty/src/mir/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,14 @@ pub enum MirLowerError {
/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
struct DropScopeToken;
impl DropScopeToken {
fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId {
fn pop_and_drop(
self,
ctx: &mut MirLowerCtx<'_>,
current: BasicBlockId,
span: MirSpan,
) -> BasicBlockId {
std::mem::forget(self);
ctx.pop_drop_scope_internal(current)
ctx.pop_drop_scope_internal(current, span)
}

/// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
Expand Down Expand Up @@ -582,7 +587,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
let scope = this.push_drop_scope();
if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
current = scope.pop_and_drop(this, current);
current = scope.pop_and_drop(this, current, body.into());
this.set_goto(current, begin, expr_id.into());
} else {
scope.pop_assume_dropped(this);
Expand Down Expand Up @@ -720,7 +725,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
.ok_or(MirLowerError::ContinueWithoutLoop)?,
};
let begin = loop_data.begin;
current = self.drop_until_scope(loop_data.drop_scope_index, current);
current =
self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());
self.set_goto(current, begin, expr_id.into());
Ok(None)
}
Expand Down Expand Up @@ -759,7 +765,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.current_loop_blocks.as_ref().unwrap().drop_scope_index,
),
};
current = self.drop_until_scope(drop_scope, current);
current = self.drop_until_scope(drop_scope, current, expr_id.into());
self.set_goto(current, end, expr_id.into());
Ok(None)
}
Expand All @@ -773,7 +779,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None);
}
}
current = self.drop_until_scope(0, current);
current = self.drop_until_scope(0, current, expr_id.into());
self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None)
}
Expand Down Expand Up @@ -1782,7 +1788,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None);
};
self.push_fake_read(c, p, expr.into());
current = scope2.pop_and_drop(self, c);
current = scope2.pop_and_drop(self, c, expr.into());
}
}
}
Expand All @@ -1793,7 +1799,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
};
current = c;
}
current = scope.pop_and_drop(self, current);
current = scope.pop_and_drop(self, current, span);
Ok(Some(current))
}

Expand Down Expand Up @@ -1873,9 +1879,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}

fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
fn drop_until_scope(
&mut self,
scope_index: usize,
mut current: BasicBlockId,
span: MirSpan,
) -> BasicBlockId {
for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span);
}
current
}
Expand All @@ -1891,17 +1902,22 @@ impl<'ctx> MirLowerCtx<'ctx> {
}

/// Don't call directly
fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId {
fn pop_drop_scope_internal(
&mut self,
mut current: BasicBlockId,
span: MirSpan,
) -> BasicBlockId {
let scope = self.drop_scopes.pop().unwrap();
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span);
current
}

fn pop_drop_scope_assert_finished(
&mut self,
mut current: BasicBlockId,
span: MirSpan,
) -> Result<BasicBlockId> {
current = self.pop_drop_scope_internal(current);
current = self.pop_drop_scope_internal(current, span);
if !self.drop_scopes.is_empty() {
implementation_error!("Mismatched count between drop scope push and pops");
}
Expand All @@ -1912,20 +1928,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
&mut self,
scope: &DropScope,
current: &mut Idx<BasicBlock>,
span: MirSpan,
) {
for &l in scope.locals.iter().rev() {
if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
let prev = std::mem::replace(current, self.new_basic_block());
self.set_terminator(
prev,
TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
MirSpan::Unknown,
span,
);
}
self.push_statement(
*current,
StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
);
self.push_statement(*current, StatementKind::StorageDead(l).with_span(span));
}
}
}
Expand Down Expand Up @@ -2002,7 +2016,7 @@ pub fn mir_body_for_closure_query(
|_| true,
)?;
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
let current = ctx.pop_drop_scope_assert_finished(current)?;
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
}
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
Expand Down Expand Up @@ -2146,7 +2160,7 @@ pub fn lower_to_mir(
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
};
if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
let current = ctx.pop_drop_scope_assert_finished(current)?;
let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
}
Ok(ctx.result)
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> {
let indent = mem::take(&mut self.indent);
let mut ctx = MirPrettyCtx {
body: &body,
local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(),
local_to_binding: body.local_to_binding_map(),
result,
indent,
..*self
Expand All @@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> {
}

fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect();
let local_to_binding = body.local_to_binding_map();
MirPrettyCtx {
body,
db,
Expand Down
5 changes: 3 additions & 2 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ use hir_ty::{
known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
mir::interpret_mir,
primitive::UintTy,
traits::FnTrait,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
Expand Down Expand Up @@ -129,9 +129,10 @@ pub use {
hir_ty::{
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
layout::LayoutError,
mir::MirEvalError,
PointerCast, Safety,
},
// FIXME: Properly encapsulate mir
hir_ty::{mir, Interner as ChalkTyInterner},
};

// These are negative re-exports: pub using these names is forbidden, they
Expand Down
9 changes: 8 additions & 1 deletion crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod discriminant;
mod fn_lifetime_fn;
mod implicit_static;
mod param_name;
mod implicit_drop;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig {
Expand All @@ -45,6 +46,7 @@ pub struct InlayHintsConfig {
pub closure_return_type_hints: ClosureReturnTypeHints,
pub closure_capture_hints: bool,
pub binding_mode_hints: bool,
pub implicit_drop_hints: bool,
pub lifetime_elision_hints: LifetimeElisionHints,
pub param_names_for_lifetime_elision_hints: bool,
pub hide_named_constructor_hints: bool,
Expand Down Expand Up @@ -124,6 +126,7 @@ pub enum InlayKind {
Lifetime,
Parameter,
Type,
Drop,
}

#[derive(Debug)]
Expand Down Expand Up @@ -503,7 +506,10 @@ fn hints(
ast::Item(it) => match it {
// FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
ast::Item::Impl(_) => None,
ast::Item::Fn(it) => fn_lifetime_fn::hints(hints, config, it),
ast::Item::Fn(it) => {
implicit_drop::hints(hints, sema, config, &it);
fn_lifetime_fn::hints(hints, config, it)
},
// static type elisions
ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)),
ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)),
Expand Down Expand Up @@ -591,6 +597,7 @@ mod tests {
max_length: None,
closing_brace_hints_min_lines: None,
fields_to_resolve: InlayFieldsToResolve::empty(),
implicit_drop_hints: false,
};
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
type_hints: true,
Expand Down

0 comments on commit 7ef2be2

Please sign in to comment.