mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 14:23:45 +00:00
Begin experimental support for pin reborrowing
This commit adds basic support for reborrowing `Pin` types in argument position. At the moment it only supports reborrowing `Pin<&mut T>` as `Pin<&mut T>` by inserting a call to `Pin::as_mut()`, and only in argument position (not as the receiver in a method call).
This commit is contained in:
parent
13b5a4e43b
commit
7b7992fbcf
@ -207,7 +207,7 @@ borrowck_simd_intrinsic_arg_const =
|
||||
*[other] {$arg}th
|
||||
} argument of `{$intrinsic}` is required to be a `const` item
|
||||
|
||||
borrowck_suggest_create_freash_reborrow =
|
||||
borrowck_suggest_create_fresh_reborrow =
|
||||
consider reborrowing the `Pin` instead of moving it
|
||||
|
||||
borrowck_suggest_iterate_over_slice =
|
||||
|
@ -415,7 +415,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
|
||||
span: Span,
|
||||
},
|
||||
#[suggestion(
|
||||
borrowck_suggest_create_freash_reborrow,
|
||||
borrowck_suggest_create_fresh_reborrow,
|
||||
applicability = "maybe-incorrect",
|
||||
code = ".as_mut()",
|
||||
style = "verbose"
|
||||
|
@ -558,6 +558,8 @@ declare_features! (
|
||||
(unstable, optimize_attribute, "1.34.0", Some(54882)),
|
||||
/// Allows specifying nop padding on functions for dynamic patching.
|
||||
(unstable, patchable_function_entry, "1.81.0", Some(123115)),
|
||||
/// Experimental features that make `Pin` more ergonomic.
|
||||
(incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)),
|
||||
/// Allows postfix match `expr.match { ... }`
|
||||
(unstable, postfix_match, "1.79.0", Some(121618)),
|
||||
/// Allows macro attributes on expressions, statements and non-inline modules.
|
||||
|
@ -395,6 +395,7 @@ language_item_table! {
|
||||
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
|
||||
|
||||
PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
PinAsMut, sym::pin_as_mut, as_mut_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
|
||||
RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
|
||||
|
@ -214,6 +214,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||
ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
|
||||
return self.coerce_dyn_star(a, b, predicates, region);
|
||||
}
|
||||
ty::Adt(pin, _)
|
||||
if self.tcx.features().pin_ergonomics
|
||||
&& pin.did() == self.tcx.lang_items().pin_type().unwrap() =>
|
||||
{
|
||||
return self.coerce_pin(a, b);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -774,6 +780,59 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Applies reborrowing for `Pin`
|
||||
///
|
||||
/// We currently only support reborrowing `Pin<&mut T>` as `Pin<&mut T>`. This is accomplished
|
||||
/// by inserting a call to `Pin::as_mut` during MIR building.
|
||||
///
|
||||
/// In the future we might want to support other reborrowing coercions, such as:
|
||||
/// - `Pin<&mut T>` as `Pin<&T>`
|
||||
/// - `Pin<&T>` as `Pin<&T>`
|
||||
/// - `Pin<Box<T>>` as `Pin<&T>`
|
||||
/// - `Pin<Box<T>>` as `Pin<&mut T>`
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
fn coerce_pin(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
// We need to make sure the two types are compatible for coercion.
|
||||
// Then we will build a ReborrowPin adjustment and return that as an InferOk.
|
||||
|
||||
// Right now we can only reborrow if this is a `Pin<&mut T>`.
|
||||
let can_reborrow = |ty: Ty<'tcx>| {
|
||||
// Get the T out of Pin<T>
|
||||
let ty = match ty.kind() {
|
||||
ty::Adt(pin, args) if pin.did() == self.tcx.lang_items().pin_type().unwrap() => {
|
||||
args[0].expect_ty()
|
||||
}
|
||||
_ => {
|
||||
debug!("can't reborrow {:?} as pinned", ty);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
// Make sure the T is something we understand (just `&mut U` for now)
|
||||
match ty.kind() {
|
||||
ty::Ref(region, ty, ty::Mutability::Mut) => Some((*region, *ty)),
|
||||
_ => {
|
||||
debug!("can't reborrow pin of inner type {:?}", ty);
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (_, _a_ty) = can_reborrow(a).ok_or(TypeError::Mismatch)?;
|
||||
let (b_region, _b_ty) = can_reborrow(b).ok_or(TypeError::Mismatch)?;
|
||||
|
||||
// To complete the reborrow, we need to make sure we can unify the inner types, and if so we
|
||||
// add the adjustments.
|
||||
self.unify_and(a, b, |_inner_ty| {
|
||||
vec![Adjustment {
|
||||
kind: Adjust::ReborrowPin(AutoBorrow::Ref(
|
||||
b_region,
|
||||
AutoBorrowMutability::Mut { allow_two_phase_borrow: AllowTwoPhase::No },
|
||||
)),
|
||||
target: b,
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
fn coerce_from_safe_fn<F, G>(
|
||||
&self,
|
||||
a: Ty<'tcx>,
|
||||
|
@ -780,6 +780,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
||||
adjustment::Adjust::Borrow(ref autoref) => {
|
||||
self.walk_autoref(expr, &place_with_id, autoref);
|
||||
}
|
||||
|
||||
adjustment::Adjust::ReborrowPin(ref autoref) => {
|
||||
// Reborrowing a Pin is like a combinations of a deref and a borrow, so we do
|
||||
// both.
|
||||
let bk = match autoref {
|
||||
adjustment::AutoBorrow::Ref(_, m) => {
|
||||
ty::BorrowKind::from_mutbl((*m).into())
|
||||
}
|
||||
adjustment::AutoBorrow::RawPtr(m) => ty::BorrowKind::from_mutbl(*m),
|
||||
};
|
||||
self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
|
||||
|
||||
self.walk_autoref(expr, &place_with_id, autoref);
|
||||
}
|
||||
}
|
||||
place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
|
||||
}
|
||||
@ -1284,6 +1298,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
||||
adjustment::Adjust::NeverToAny
|
||||
| adjustment::Adjust::Pointer(_)
|
||||
| adjustment::Adjust::Borrow(_)
|
||||
| adjustment::Adjust::ReborrowPin(_)
|
||||
| adjustment::Adjust::DynStar => {
|
||||
// Result is an rvalue.
|
||||
Ok(self.cat_rvalue(expr.hir_id, target))
|
||||
|
@ -104,6 +104,9 @@ pub enum Adjust<'tcx> {
|
||||
|
||||
/// Cast into a dyn* object.
|
||||
DynStar,
|
||||
|
||||
/// Take a Pin<Ptr> and call either as_mut() or as_ref() to get a Pin<&mut T> or Pin<&T>.
|
||||
ReborrowPin(AutoBorrow<'tcx>),
|
||||
}
|
||||
|
||||
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
|
||||
|
@ -74,6 +74,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
self.thir.exprs.push(expr)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, expr, span))]
|
||||
fn apply_adjustment(
|
||||
&mut self,
|
||||
hir_expr: &'tcx hir::Expr<'tcx>,
|
||||
@ -146,6 +147,50 @@ impl<'tcx> Cx<'tcx> {
|
||||
ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) }
|
||||
}
|
||||
Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) },
|
||||
Adjust::ReborrowPin(AutoBorrow::Ref(region, m)) => {
|
||||
debug!("apply ReborrowPin adjustment");
|
||||
match m {
|
||||
AutoBorrowMutability::Mut { .. } => {
|
||||
// Rewrite `$expr` as `Pin::as_mut(&mut $expr)`
|
||||
let as_mut_method =
|
||||
self.tcx().require_lang_item(rustc_hir::LangItem::PinAsMut, Some(span));
|
||||
let pin_ty_args = match expr.ty.kind() {
|
||||
ty::Adt(_, args) => args,
|
||||
_ => bug!("ReborrowPin with non-Pin type"),
|
||||
};
|
||||
let as_mut_ty =
|
||||
Ty::new_fn_def(self.tcx, as_mut_method, pin_ty_args.into_iter());
|
||||
|
||||
let ty = Ty::new_ref(self.tcx, region, expr.ty, ty::Mutability::Mut);
|
||||
let arg = ExprKind::Borrow {
|
||||
borrow_kind: BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
|
||||
arg: self.thir.exprs.push(expr),
|
||||
};
|
||||
debug!(?arg, "borrow arg");
|
||||
let arg = self.thir.exprs.push(Expr { temp_lifetime, ty, span, kind: arg });
|
||||
|
||||
let kind = ExprKind::Call {
|
||||
ty: as_mut_ty,
|
||||
fun: self.thir.exprs.push(Expr {
|
||||
temp_lifetime,
|
||||
ty: as_mut_ty,
|
||||
span,
|
||||
kind: ExprKind::ZstLiteral { user_ty: None },
|
||||
}),
|
||||
args: Box::new([arg]),
|
||||
from_hir_call: true,
|
||||
fn_span: span,
|
||||
};
|
||||
debug!(?kind);
|
||||
kind
|
||||
}
|
||||
AutoBorrowMutability::Not => {
|
||||
// FIXME: We need to call Pin::as_ref on the expression
|
||||
bug!("ReborrowPin with shared reference is not implemented yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
Adjust::ReborrowPin(AutoBorrow::RawPtr(_)) => bug!("ReborrowPin with raw pointer"),
|
||||
};
|
||||
|
||||
Expr { temp_lifetime, ty: adjustment.target, span, kind }
|
||||
|
@ -1418,6 +1418,8 @@ symbols! {
|
||||
pic,
|
||||
pie,
|
||||
pin,
|
||||
pin_as_mut,
|
||||
pin_ergonomics,
|
||||
platform_intrinsics,
|
||||
plugin,
|
||||
plugin_registrar,
|
||||
|
@ -1408,6 +1408,7 @@ impl<Ptr: DerefMut> Pin<Ptr> {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(not(bootstrap), lang = "pin_as_mut")]
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
#[inline(always)]
|
||||
pub fn as_mut(&mut self) -> Pin<&mut Ptr::Target> {
|
||||
|
27
tests/ui/async-await/pin-reborrow-arg.rs
Normal file
27
tests/ui/async-await/pin-reborrow-arg.rs
Normal file
@ -0,0 +1,27 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(pin_ergonomics)]
|
||||
#![allow(dead_code, incomplete_features)]
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn baz(self: Pin<&mut Self>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn foo(_: Pin<&mut Foo>) {
|
||||
}
|
||||
|
||||
fn bar(mut x: Pin<&mut Foo>) {
|
||||
foo(x);
|
||||
foo(x); // for this to work we need to automatically reborrow,
|
||||
// as if the user had written `foo(x.as_mut())`.
|
||||
|
||||
Foo::baz(x);
|
||||
Foo::baz(x);
|
||||
}
|
||||
|
||||
fn main() {}
|
24
tests/ui/async-await/pin-reborrow-self.rs
Normal file
24
tests/ui/async-await/pin-reborrow-self.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//@ check-pass
|
||||
//@ignore-test
|
||||
|
||||
// Currently ignored due to self reborrowing not being implemented for Pin
|
||||
|
||||
#![feature(pin_ergonomics)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn foo(self: Pin<&mut Self>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn bar(x: Pin<&mut Foo>) {
|
||||
x.foo();
|
||||
x.foo(); // for this to work we need to automatically reborrow,
|
||||
// as if the user had written `x.as_mut().foo()`.
|
||||
}
|
||||
|
||||
fn main() {}
|
15
tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
Normal file
15
tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#![allow(dead_code, incomplete_features)]
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
struct Foo;
|
||||
|
||||
fn foo(_: Pin<&mut Foo>) {
|
||||
}
|
||||
|
||||
fn bar(mut x: Pin<&mut Foo>) {
|
||||
foo(x);
|
||||
foo(x); //~ ERROR use of moved value: `x`
|
||||
}
|
||||
|
||||
fn main() {}
|
21
tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
Normal file
21
tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
Normal file
@ -0,0 +1,21 @@
|
||||
error[E0382]: use of moved value: `x`
|
||||
--> $DIR/feature-gate-pin_ergonomics.rs:12:9
|
||||
|
|
||||
LL | fn bar(mut x: Pin<&mut Foo>) {
|
||||
| ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
|
||||
LL | foo(x);
|
||||
| - value moved here
|
||||
LL | foo(x);
|
||||
| ^ value used here after move
|
||||
|
|
||||
note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary
|
||||
--> $DIR/feature-gate-pin_ergonomics.rs:7:11
|
||||
|
|
||||
LL | fn foo(_: Pin<&mut Foo>) {
|
||||
| --- ^^^^^^^^^^^^^ this parameter takes ownership of the value
|
||||
| |
|
||||
| in this function
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0382`.
|
Loading…
Reference in New Issue
Block a user