Skip to content

Commit

Permalink
Account for UseCloned on expr_use_visitor
Browse files Browse the repository at this point in the history
  • Loading branch information
spastorino committed Feb 20, 2025
1 parent 98d51f1 commit a5cb5f9
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 2 deletions.
48 changes: 47 additions & 1 deletion compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ pub trait Delegate<'tcx> {
/// the id of the binding in the pattern `pat`.
fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);

/// The value found at `place` is moved, depending
/// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
///
/// Use of a `Copy` type in a ByValue context is considered a use
/// by `ImmBorrow` and `borrow` is called instead. This is because
/// a shared borrow is the "minimum access" that would be needed
/// to perform a copy.
///
///
/// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
/// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
/// id will be the id of the expression `expr` but the place itself will have
/// the id of the binding in the pattern `pat`.
fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);

/// The value found at `place` is being borrowed with kind `bk`.
/// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
fn borrow(
Expand Down Expand Up @@ -91,6 +106,10 @@ impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D {
(**self).consume(place_with_id, diag_expr_id)
}

fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
(**self).use_cloned(place_with_id, diag_expr_id)
}

fn borrow(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
Expand Down Expand Up @@ -143,6 +162,8 @@ pub trait TypeInformationCtxt<'tcx> {

fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool;

fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool;

fn body_owner_def_id(&self) -> LocalDefId;

fn tcx(&self) -> TyCtxt<'tcx>;
Expand Down Expand Up @@ -184,6 +205,10 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
self.infcx.type_is_copy_modulo_regions(self.param_env, ty)
}

fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
self.infcx.type_is_use_cloned_modulo_regions(self.param_env, ty)
}

fn body_owner_def_id(&self) -> LocalDefId {
self.body_id
}
Expand Down Expand Up @@ -230,6 +255,10 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
self.0.type_is_copy_modulo_regions(ty)
}

fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
self.0.type_is_use_cloned_modulo_regions(ty)
}

fn body_owner_def_id(&self) -> LocalDefId {
self.1
}
Expand Down Expand Up @@ -313,6 +342,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
Ok(())
}

pub fn consume_or_clone_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
debug!("consume_or_clone_expr(expr={:?})", expr);

let place_with_id = self.cat_expr(expr)?;

if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
self.delegate.borrow_mut().copy(&place_with_id, place_with_id.hir_id);
} else if self.cx.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) {
self.delegate.borrow_mut().use_cloned(&place_with_id, place_with_id.hir_id);
} else {
self.delegate.borrow_mut().consume(&place_with_id, place_with_id.hir_id);
}

self.walk_expr(expr)?;
Ok(())
}

fn mutate_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
let place_with_id = self.cat_expr(expr)?;
self.delegate.borrow_mut().mutate(&place_with_id, place_with_id.hir_id);
Expand Down Expand Up @@ -367,7 +413,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
}

hir::ExprKind::Use(expr, _) => {
self.consume_expr(expr)?;
self.consume_or_clone_expr(expr)?;
}

hir::ExprKind::MethodCall(.., receiver, args, _) => {
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_hir_typeck/src/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,21 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
));
}

#[instrument(skip(self), level = "debug")]
fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);

self.capture_information.push((
place_with_id.place.clone(),
ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
capture_kind: ty::UpvarCapture::ByUse,
},
));
}

#[instrument(skip(self), level = "debug")]
fn borrow(
&mut self,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,10 @@ impl<'tcx> LateContext<'tcx> {
self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty)
}

pub fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty)
}

/// Gets the type-checking results for the current body,
/// or `None` if outside a body.
pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,11 @@ rustc_queries! {
query is_copy_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Copy`", env.value }
}
/// Trait selection queries. These are best used by invoking `ty.is_use_cloned_modulo_regions()`,
/// `ty.is_use_cloned()`, etc, since that will prune the environment where possible.
query is_use_cloned_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Copy`", env.value }
}
/// Query backing `Ty::is_sized`.
query is_sized_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Sized`", env.value }
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ impl<'tcx> TyCtxt<'tcx> {
ty.is_trivially_pure_clone_copy() || self.is_copy_raw(typing_env.as_query_input(ty))
}

/// Checks whether `ty: UseCloned` holds while ignoring region constraints.
///
/// This function should not be used if there is an `InferCtxt` available.
/// Use `InferCtxt::type_is_copy_modulo_regions` instead.
pub fn type_is_use_cloned_modulo_regions(
self,
typing_env: ty::TypingEnv<'tcx>,
ty: Ty<'tcx>,
) -> bool {
ty.is_trivially_pure_clone_copy() || self.is_use_cloned_raw(typing_env.as_query_input(ty))
}

/// Returns the deeply last field of nested structures, or the same type if
/// not a structure at all. Corresponds to the only possible unsized field,
/// and its type can be used to determine unsizing strategy.
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_ty_utils/src/common_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty
is_item_raw(tcx, query, LangItem::Copy)
}

fn is_use_cloned_raw<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
) -> bool {
is_item_raw(tcx, query, LangItem::UseCloned)
}

fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
is_item_raw(tcx, query, LangItem::Sized)
}
Expand All @@ -33,5 +40,12 @@ fn is_item_raw<'tcx>(
}

pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers };
*providers = Providers {
is_copy_raw,
is_use_cloned_raw,
is_sized_raw,
is_freeze_raw,
is_unpin_raw,
..*providers
};
}

0 comments on commit a5cb5f9

Please sign in to comment.