Account for UseCloned on expr_use_visitor

This commit is contained in:
Santiago Pastorino 2025-02-20 14:34:49 -03:00
parent edcbc9b535
commit b43b700250
No known key found for this signature in database
GPG Key ID: 8131A24E0C79EFAF
6 changed files with 98 additions and 2 deletions

View File

@ -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 used, depending
/// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
///
/// Use of a `Copy` type in a ByUse 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(
@ -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>,
@ -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>;
@ -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
}
@ -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
}
@ -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);
@ -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, _) => {

View File

@ -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,

View File

@ -683,6 +683,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>> {

View File

@ -1533,6 +1533,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 `UseCloned`", 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 }

View File

@ -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.

View File

@ -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)
}
@ -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
};
}