Rollup merge of #134797 - spastorino:ergonomic-ref-counting-1, r=nikomatsakis

Ergonomic ref counting

This is an experimental first version of ergonomic ref counting.

This first version implements most of the RFC but doesn't implement any of the optimizations. This was left for following iterations.

RFC: https://github.com/rust-lang/rfcs/pull/3680
Tracking issue: https://github.com/rust-lang/rust/issues/132290
Project goal: https://github.com/rust-lang/rust-project-goals/issues/107

r? ```@nikomatsakis```
This commit is contained in:
Matthias Krüger 2025-03-07 19:15:33 +01:00 committed by GitHub
commit f5a143f796
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
119 changed files with 1401 additions and 79 deletions

View File

@ -1399,6 +1399,7 @@ impl Expr {
// Never need parens
ExprKind::Array(_)
| ExprKind::Await(..)
| ExprKind::Use(..)
| ExprKind::Block(..)
| ExprKind::Call(..)
| ExprKind::ConstBlock(_)
@ -1588,6 +1589,8 @@ pub enum ExprKind {
Gen(CaptureBy, P<Block>, GenBlockKind, Span),
/// An await expression (`my_future.await`). Span is of await keyword.
Await(P<Expr>, Span),
/// A use expression (`x.use`). Span is of use keyword.
Use(P<Expr>, Span),
/// A try block (`try { ... }`).
TryBlock(P<Block>),
@ -1757,8 +1760,17 @@ pub enum CaptureBy {
/// The span of the `move` keyword.
move_kw: Span,
},
/// `move` keyword was not specified.
/// `move` or `use` keywords were not specified.
Ref,
/// `use |x| y + x`.
///
/// Note that if you have a regular closure like `|| x.use`, this will *not* result
/// in a `Use` capture. Instead, the `ExprUseVisitor` will look at the type
/// of `x` and treat `x.use` as either a copy/clone/move as appropriate.
Use {
/// The span of the `use` keyword.
use_kw: Span,
},
}
/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.

View File

@ -1745,6 +1745,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
vis.visit_expr(expr);
vis.visit_span(await_kw_span);
}
ExprKind::Use(expr, use_kw_span) => {
vis.visit_expr(expr);
vis.visit_span(use_kw_span);
}
ExprKind::Assign(el, er, span) => {
vis.visit_expr(el);
vis.visit_expr(er);
@ -1895,6 +1899,9 @@ fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
CaptureBy::Value { move_kw } => {
vis.visit_span(move_kw);
}
CaptureBy::Use { use_kw } => {
vis.visit_span(use_kw);
}
}
}

View File

@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
Assign(e, _, _)
| AssignOp(_, e, _)
| Await(e, _)
| Use(e, _)
| Binary(_, e, _)
| Call(e, _)
| Cast(e, _)
@ -224,6 +225,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Lit(_)
| Type(_, _)
| Await(_, _)
| Use(_, _)
| Field(_, _)
| Index(_, _, _)
| Underscore

View File

@ -1211,6 +1211,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
}
ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)),
ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)),
ExprKind::Use(expr, _span) => try_visit!(visitor.visit_expr(expr)),
ExprKind::Assign(lhs, rhs, _span) => {
try_visit!(visitor.visit_expr(lhs));
try_visit!(visitor.visit_expr(rhs));

View File

@ -207,6 +207,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
},
),
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
ExprKind::Closure(box Closure {
binder,
capture_clause,
@ -1067,6 +1068,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
hir::ExprKind::Use(self.lower_expr(expr), use_kw_span)
}
fn lower_expr_closure(
&mut self,
binder: &ClosureBinder,

View File

@ -489,6 +489,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(ergonomic_clones, "ergonomic clones are experimental");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");

View File

@ -574,6 +574,14 @@ impl<'a> State<'a> {
);
self.word(".await");
}
ast::ExprKind::Use(expr, _) => {
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".use");
}
ast::ExprKind::Assign(lhs, rhs, _) => {
self.print_expr_cond_paren(
lhs,
@ -885,6 +893,7 @@ impl<'a> State<'a> {
fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
match capture_clause {
ast::CaptureBy::Value { .. } => self.word_space("move"),
ast::CaptureBy::Use { .. } => self.word_space("use"),
ast::CaptureBy::Ref => {}
}
}

View File

@ -403,6 +403,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.expect_closure();
let span = match capture_clause {
rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(),
rustc_hir::CaptureBy::Use { use_kw } => use_kw.shrink_to_lo(),
rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(),
};
diag.span_suggestion_verbose(

View File

@ -823,7 +823,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
) => {
capture_reason = format!("mutable borrow of `{upvar}`");
}
ty::UpvarCapture::ByValue => {
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
capture_reason = format!("possible mutation of `{upvar}`");
}
_ => bug!("upvar `{upvar}` borrowed, but not mutably"),

View File

@ -1490,14 +1490,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
let stmt = &bbd.statements[loc.statement_index];
debug!("temporary assigned in: stmt={:?}", stmt);
if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind
{
propagate_closure_used_mut_place(self, source);
} else {
bug!(
"closures should only capture user variables \
match stmt.kind {
StatementKind::Assign(box (
_,
Rvalue::Ref(_, _, source)
| Rvalue::Use(Operand::Copy(source) | Operand::Move(source)),
)) => {
propagate_closure_used_mut_place(self, source);
}
_ => {
bug!(
"closures should only capture user variables \
or references to user variables"
);
);
}
}
}
_ => propagate_closure_used_mut_place(self, place),

View File

@ -297,6 +297,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::AssignOp(_, _, _)
| ExprKind::Gen(_, _, _, _)
| ExprKind::Await(_, _)
| ExprKind::Use(_, _)
| ExprKind::Block(_, _)
| ExprKind::Break(_, _)
| ExprKind::Closure(_)

View File

@ -474,6 +474,8 @@ declare_features! (
(unstable, doc_masked, "1.21.0", Some(44027)),
/// Allows `dyn* Trait` objects.
(incomplete, dyn_star, "1.65.0", Some(102425)),
/// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
(incomplete, ergonomic_clones, "CURRENT_RUSTC_VERSION", Some(132290)),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(unstable, exhaustive_patterns, "1.13.0", Some(51085)),
/// Allows explicit tail calls via `become` expression.

View File

@ -2166,6 +2166,7 @@ impl Expr<'_> {
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Use(..)
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
ExprKind::DropTemps(expr, ..) => expr.precedence(),
@ -2212,6 +2213,7 @@ impl Expr<'_> {
ExprKind::Path(QPath::TypeRelative(..))
| ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Use(..)
| ExprKind::Struct(..)
| ExprKind::Tup(..)
| ExprKind::If(..)
@ -2285,7 +2287,9 @@ impl Expr<'_> {
pub fn can_have_side_effects(&self) -> bool {
match self.peel_drop_temps().kind {
ExprKind::Path(_) | ExprKind::Lit(_) | ExprKind::OffsetOf(..) => false,
ExprKind::Path(_) | ExprKind::Lit(_) | ExprKind::OffsetOf(..) | ExprKind::Use(..) => {
false
}
ExprKind::Type(base, _)
| ExprKind::Unary(_, base)
| ExprKind::Field(base, _)
@ -2547,6 +2551,8 @@ pub enum ExprKind<'hir> {
///
/// [`type_dependent_def_id`]: ../../rustc_middle/ty/struct.TypeckResults.html#method.type_dependent_def_id
MethodCall(&'hir PathSegment<'hir>, &'hir Expr<'hir>, &'hir [Expr<'hir>], Span),
/// An use expression (e.g., `var.use`).
Use(&'hir Expr<'hir>, Span),
/// A tuple (e.g., `(a, b, c, d)`).
Tup(&'hir [Expr<'hir>]),
/// A binary operation (e.g., `a + b`, `a * b`).

View File

@ -821,6 +821,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
try_visit!(visitor.visit_expr(receiver));
walk_list!(visitor, visit_expr, arguments);
}
ExprKind::Use(expr, _) => {
try_visit!(visitor.visit_expr(expr));
}
ExprKind::Binary(_, ref left_expression, ref right_expression) => {
try_visit!(visitor.visit_expr(left_expression));
try_visit!(visitor.visit_expr(right_expression));

View File

@ -171,6 +171,7 @@ language_item_table! {
Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
UseCloned, sym::use_cloned, use_cloned_trait, Target::Trait, GenericRequirement::None;
Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
/// The associated item of the `DiscriminantKind` trait.

View File

@ -1470,6 +1470,10 @@ impl<'a> State<'a> {
hir::ExprKind::MethodCall(segment, receiver, args, _) => {
self.print_expr_method_call(segment, receiver, args);
}
hir::ExprKind::Use(expr, _) => {
self.print_expr(expr);
self.word(".use");
}
hir::ExprKind::Binary(op, lhs, rhs) => {
self.print_expr_binary(op, lhs, rhs);
}
@ -2220,6 +2224,7 @@ impl<'a> State<'a> {
fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
match capture_clause {
hir::CaptureBy::Value { .. } => self.word_space("move"),
hir::CaptureBy::Use { .. } => self.word_space("use"),
hir::CaptureBy::Ref => {}
}
}

View File

@ -362,6 +362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Any expression child of these expressions constitute reads.
ExprKind::Array(_)
| ExprKind::Call(_, _)
| ExprKind::Use(_, _)
| ExprKind::MethodCall(_, _, _, _)
| ExprKind::Tup(_)
| ExprKind::Binary(_, _, _)
@ -552,6 +553,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected),
ExprKind::Block(body, _) => self.check_expr_block(body, expected),
ExprKind::Call(callee, args) => self.check_expr_call(expr, callee, args, expected),
ExprKind::Use(used_expr, _) => self.check_expr_use(used_expr, expected),
ExprKind::MethodCall(segment, receiver, args, _) => {
self.check_expr_method_call(expr, segment, receiver, args, expected)
}
@ -1616,6 +1618,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}
/// Checks use `x.use`.
fn check_expr_use(
&self,
used_expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
self.check_expr_with_expectation(used_expr, expected)
}
fn check_expr_cast(
&self,
e: &'tcx hir::Expr<'tcx>,

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
}
@ -295,6 +324,24 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
}
}
pub fn consume_clone_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
debug!("delegate_consume_or_clone(place_with_id={:?})", place_with_id);
// `x.use` will do one of the following
// * if it implements `Copy`, it will be a copy
// * if it implements `UseCloned`, it will be a call to `clone`
// * otherwise, it is a move
//
// we do a conservative approximation of this, treating it as a move unless we know that it implements copy or `UseCloned`
if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
self.delegate.borrow_mut().copy(place_with_id, diag_expr_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, diag_expr_id);
} else {
self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
}
}
fn consume_exprs(&self, exprs: &[hir::Expr<'_>]) -> Result<(), Cx::Error> {
for expr in exprs {
self.consume_expr(expr)?;
@ -313,6 +360,15 @@ 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)?;
self.consume_clone_or_copy(&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);
@ -366,6 +422,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.consume_exprs(args)?;
}
hir::ExprKind::Use(expr, _) => {
self.consume_or_clone_expr(expr)?;
}
hir::ExprKind::MethodCall(.., receiver, args, _) => {
// callee.m(args)
self.consume_expr(receiver)?;
@ -1085,6 +1145,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
ty::UpvarCapture::ByValue => {
self.consume_or_copy(&place_with_id, place_with_id.hir_id);
}
ty::UpvarCapture::ByUse => {
self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
}
ty::UpvarCapture::ByRef(upvar_borrow) => {
self.delegate.borrow_mut().borrow(
&place_with_id,
@ -1386,6 +1449,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
hir::ExprKind::AddrOf(..)
| hir::ExprKind::Call(..)
| hir::ExprKind::Use(..)
| hir::ExprKind::Assign(..)
| hir::ExprKind::AssignOp(..)
| hir::ExprKind::Closure { .. }

View File

@ -671,6 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (place, capture_kind) = match capture_clause {
hir::CaptureBy::Value { .. } => adjust_for_move_closure(place, capture_kind),
hir::CaptureBy::Use { .. } => adjust_for_use_closure(place, capture_kind),
hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind),
};
@ -1165,7 +1166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = match closure_clause {
hir::CaptureBy::Value { .. } => ty, // For move closure the capture kind should be by value
hir::CaptureBy::Ref => {
hir::CaptureBy::Ref | hir::CaptureBy::Use { .. } => {
// For non move closure the capture kind is the max capture kind of all captures
// according to the ordering ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue
let mut max_capture_info = root_var_min_capture_list.first().unwrap().info;
@ -1292,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.insert(UpvarMigrationInfo::CapturingNothing { use_span: upvar.span });
return Some(diagnostics_info);
}
hir::CaptureBy::Ref => {}
hir::CaptureBy::Ref | hir::CaptureBy::Use { .. } => {}
}
return None;
@ -1305,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for captured_place in root_var_min_capture_list.iter() {
match captured_place.info.capture_kind {
// Only care about captures that are moved into the closure
ty::UpvarCapture::ByValue => {
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
projections_list.push(captured_place.place.projections.as_slice());
diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise {
source_expr: captured_place.info.path_expr_id,
@ -1689,10 +1690,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
//
// If the data will be moved out of this place, then the place will be truncated
// at the first Deref in `adjust_for_move_closure` and then moved into the closure.
//
// For example:
//
// struct Buffer<'a> {
// x: &'a String,
// y: Vec<u8>,
// }
//
// fn get<'a>(b: Buffer<'a>) -> impl Sized + 'a {
// let c = move || b.x;
// drop(b);
// c
// }
//
// Even though the closure is declared as move, when we are capturing borrowed data (in
// this case, *b.x) we prefer to capture by reference.
// Otherwise you'd get an error in 2021 immediately because you'd be trying to take
// ownership of the (borrowed) String or else you'd take ownership of b, as in 2018 and
// before, which is also an error.
hir::CaptureBy::Value { .. } if !place.deref_tys().any(Ty::is_ref) => {
ty::UpvarCapture::ByValue
}
hir::CaptureBy::Value { .. } | hir::CaptureBy::Ref => {
hir::CaptureBy::Use { .. } if !place.deref_tys().any(Ty::is_ref) => {
ty::UpvarCapture::ByUse
}
hir::CaptureBy::Value { .. } | hir::CaptureBy::Use { .. } | hir::CaptureBy::Ref => {
ty::UpvarCapture::ByRef(BorrowKind::Immutable)
}
}
@ -1927,7 +1950,7 @@ fn apply_capture_kind_on_capture_ty<'tcx>(
region: ty::Region<'tcx>,
) -> Ty<'tcx> {
match capture_kind {
ty::UpvarCapture::ByValue => ty,
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => ty,
ty::UpvarCapture::ByRef(kind) => Ty::new_ref(tcx, region, ty, kind.to_mutbl_lossy()),
}
}
@ -2023,6 +2046,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,
@ -2164,6 +2202,20 @@ fn adjust_for_move_closure(
(place, ty::UpvarCapture::ByValue)
}
/// Truncate deref of any reference.
fn adjust_for_use_closure(
mut place: Place<'_>,
mut kind: ty::UpvarCapture,
) -> (Place<'_>, ty::UpvarCapture) {
let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
if let Some(idx) = first_deref {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx);
}
(place, ty::UpvarCapture::ByUse)
}
/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
fn adjust_for_non_move_closure(
@ -2174,7 +2226,7 @@ fn adjust_for_non_move_closure(
place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
match kind {
ty::UpvarCapture::ByValue => {
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
if let Some(idx) = contains_deref {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx);
}
@ -2219,6 +2271,7 @@ fn construct_capture_kind_reason_string<'tcx>(
let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue => "ByValue".into(),
ty::UpvarCapture::ByUse => "ByUse".into(),
ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"),
};
@ -2240,6 +2293,7 @@ fn construct_capture_info_string<'tcx>(
let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue => "ByValue".into(),
ty::UpvarCapture::ByUse => "ByUse".into(),
ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"),
};
format!("{place_str} -> {capture_kind_str}")
@ -2335,8 +2389,11 @@ fn determine_capture_info(
// expressions.
let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true,
(ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse) => true,
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b,
(ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false,
(ty::UpvarCapture::ByValue, _)
| (ty::UpvarCapture::ByUse, _)
| (ty::UpvarCapture::ByRef(_), _) => false,
};
if eq_capture_kind {
@ -2346,10 +2403,20 @@ fn determine_capture_info(
}
} else {
// We select the CaptureKind which ranks higher based the following priority order:
// ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
// (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow
match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue, _) => capture_info_a,
(_, ty::UpvarCapture::ByValue) => capture_info_b,
(ty::UpvarCapture::ByUse, ty::UpvarCapture::ByValue)
| (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByUse) => {
bug!("Same capture can't be ByUse and ByValue at the same time")
}
(ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue)
| (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse)
| (ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse, ty::UpvarCapture::ByRef(_)) => {
capture_info_a
}
(ty::UpvarCapture::ByRef(_), ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse) => {
capture_info_b
}
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
match (ref_a, ref_b) {
// Take LHS:
@ -2401,7 +2468,7 @@ fn truncate_place_to_len_and_update_capture_kind<'tcx>(
}
ty::UpvarCapture::ByRef(..) => {}
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
}
place.projections.truncate(len);

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

@ -159,7 +159,10 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
ExprKind::Path(..) => false,
// Calls return rvalues.
ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) => true,
ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Use(..)
| ExprKind::Binary(..) => true,
// Inner blocks are rvalues.
ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::Block(..) => true,

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

@ -315,6 +315,14 @@ pub enum ExprKind<'tcx> {
/// (e.g. `foo(a, b)` in `x.foo(a, b)`).
fn_span: Span,
},
/// A use expression `x.use`.
ByUse {
/// The expression on which use is applied.
expr: ExprId,
/// The span of use, without the dot and receiver
/// (e.g. `use` in `x.use`).
span: Span,
},
/// A *non-overloaded* dereference.
Deref {
arg: ExprId,

View File

@ -59,6 +59,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
visitor.visit_expr(&visitor.thir()[arg]);
}
}
ByUse { expr, span: _ } => {
visitor.visit_expr(&visitor.thir()[expr]);
}
Deref { arg } => visitor.visit_expr(&visitor.thir()[arg]),
Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => {
visitor.visit_expr(&visitor.thir()[lhs]);

View File

@ -51,6 +51,9 @@ pub enum UpvarCapture {
/// depending on inference.
ByValue,
/// Upvar is captured by use. This is true when the closure is labeled `use`.
ByUse,
/// Upvar is captured by reference.
ByRef(BorrowKind),
}
@ -178,7 +181,7 @@ impl<'tcx> CapturedPlace<'tcx> {
pub fn is_by_ref(&self) -> bool {
match self.info.capture_kind {
ty::UpvarCapture::ByValue => false,
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => false,
ty::UpvarCapture::ByRef(..) => true,
}
}
@ -214,7 +217,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn closure_captures(self, def_id: LocalDefId) -> &'tcx [&'tcx ty::CapturedPlace<'tcx>] {
if !self.is_closure_like(def_id.to_def_id()) {
return &[];
};
}
self.closure_typeinfo(def_id).captures
}
}

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

@ -582,6 +582,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Yield { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::Call { .. }
| ExprKind::ByUse { .. }
| ExprKind::WrapUnsafeBinder { .. } => {
// these are not places, so we need to make a temporary.
debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));

View File

@ -572,6 +572,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
block.and(Rvalue::Use(operand))
}
ExprKind::ByUse { expr, span: _ } => {
let operand = unpack!(
block =
this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No)
);
block.and(Rvalue::Use(operand))
}
}
}

View File

@ -56,6 +56,7 @@ impl Category {
| ExprKind::RawBorrow { .. }
| ExprKind::Yield { .. }
| ExprKind::Call { .. }
| ExprKind::ByUse { .. }
| ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Array { .. }

View File

@ -4,11 +4,14 @@ use rustc_ast::{AsmMacro, InlineAsmOptions};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::thir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty};
use rustc_span::DUMMY_SP;
use rustc_span::source_map::Spanned;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::{debug, instrument};
use crate::builder::expr::category::{Category, RvalueFunc};
@ -289,6 +292,57 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.diverge_from(block);
success.unit()
}
ExprKind::ByUse { expr, span } => {
let place = unpack!(block = this.as_place(block, expr));
let ty = place.ty(&this.local_decls, this.tcx).ty;
if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
this.cfg.push_assign(
block,
source_info,
destination,
Rvalue::Use(Operand::Copy(place)),
);
block.unit()
} else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
// Convert `expr.use` to a call like `Clone::clone(&expr)`
let success = this.cfg.start_new_block();
let clone_trait = this.tcx.require_lang_item(LangItem::Clone, None);
let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
let ref_place = this.temp(ref_ty, span);
this.cfg.push_assign(
block,
source_info,
ref_place,
Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
);
this.cfg.terminate(
block,
source_info,
TerminatorKind::Call {
func,
args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
.into(),
destination,
target: Some(success),
unwind: UnwindAction::Unreachable,
call_source: CallSource::Misc,
fn_span: expr_span,
},
);
success.unit()
} else {
this.cfg.push_assign(
block,
source_info,
destination,
Rvalue::Use(Operand::Move(place)),
);
block.unit()
}
}
ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
ExprKind::Borrow { arg, borrow_kind } => {
// We don't do this in `as_rvalue` because we use `as_place`

View File

@ -37,7 +37,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
.map(|captured_place| {
let name = captured_place.to_symbol();
match captured_place.info.capture_kind {
ty::UpvarCapture::ByValue => name,
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => name,
ty::UpvarCapture::ByRef(..) => Symbol::intern(&format!("_ref__{name}")),
}
})
@ -871,7 +871,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mut projs = closure_env_projs.clone();
projs.push(ProjectionElem::Field(FieldIdx::new(i), ty));
match capture {
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
ty::UpvarCapture::ByRef(..) => {
projs.push(ProjectionElem::Deref);
}

View File

@ -451,6 +451,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::Tuple { .. }
| ExprKind::Unary { .. }
| ExprKind::Call { .. }
| ExprKind::ByUse { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Break { .. }

View File

@ -464,6 +464,10 @@ impl<'tcx> ThirBuildCx<'tcx> {
}
}
hir::ExprKind::Use(expr, span) => {
ExprKind::ByUse { expr: self.mirror_expr(expr), span }
}
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, arg) => {
ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: self.mirror_expr(arg) }
}
@ -648,7 +652,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
}
},
hir::ExprKind::Closure { .. } => {
hir::ExprKind::Closure(hir::Closure { .. }) => {
let closure_ty = self.typeck_results.expr_ty(expr);
let (def_id, args, movability) = match *closure_ty.kind() {
ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None),
@ -1248,6 +1252,17 @@ impl<'tcx> ThirBuildCx<'tcx> {
match upvar_capture {
ty::UpvarCapture::ByValue => captured_place_expr,
ty::UpvarCapture::ByUse => {
let span = captured_place_expr.span;
let expr_id = self.thir.exprs.push(captured_place_expr);
Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty: upvar_ty,
span: closure_expr.span,
kind: ExprKind::ByUse { expr: expr_id, span },
}
}
ty::UpvarCapture::ByRef(upvar_borrow) => {
let borrow_kind = match upvar_borrow {
ty::BorrowKind::Immutable => BorrowKind::Shared,

View File

@ -345,6 +345,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
| Borrow { .. }
| Box { .. }
| Call { .. }
| ByUse { .. }
| Closure { .. }
| ConstBlock { .. }
| ConstParam { .. }

View File

@ -246,6 +246,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
print_indented!(self, "}", depth_lvl);
}
ByUse { expr, span } => {
print_indented!(self, "ByUse {", depth_lvl);
print_indented!(self, "expr:", depth_lvl + 1);
self.print_expr(*expr, depth_lvl + 2);
print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
print_indented!(self, "}", depth_lvl);
}
Deref { arg } => {
print_indented!(self, "Deref {", depth_lvl);
self.print_expr(*arg, depth_lvl + 1);

View File

@ -170,7 +170,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
// this when building the field projection in the MIR body later on.
let mut parent_capture_ty = parent_capture.place.ty();
parent_capture_ty = match parent_capture.info.capture_kind {
ty::UpvarCapture::ByValue => parent_capture_ty,
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => parent_capture_ty,
ty::UpvarCapture::ByRef(kind) => Ty::new_ref(
tcx,
tcx.lifetimes.re_erased,

View File

@ -26,6 +26,11 @@ parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 20
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
.suggestion = try switching the order
parse_async_use_block_in_2015 = `async use` blocks are only allowed in Rust 2018 or later
parse_async_use_order_incorrect = the order of `use` and `async` is incorrect
.suggestion = try switching the order
parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns
.suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @`
@ -348,6 +353,9 @@ parse_incorrect_use_of_await = incorrect use of `await`
parse_incorrect_use_of_await_postfix_suggestion = `await` is a postfix operation
parse_incorrect_use_of_use = incorrect use of `use`
.parentheses_suggestion = `use` is not a method call, try removing the parentheses
parse_incorrect_visibility_restriction = incorrect visibility restriction
.help = some possible visibility restrictions are:
`pub(crate)`: visible only on the current crate

View File

@ -106,6 +106,19 @@ pub(crate) struct IncorrectUseOfAwait {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_incorrect_use_of_use)]
pub(crate) struct IncorrectUseOfUse {
#[primary_span]
#[suggestion(
parse_parentheses_suggestion,
style = "verbose",
code = "",
applicability = "machine-applicable"
)]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
parse_incorrect_use_of_await_postfix_suggestion,
@ -1499,6 +1512,14 @@ pub(crate) struct AsyncMoveOrderIncorrect {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_async_use_order_incorrect)]
pub(crate) struct AsyncUseOrderIncorrect {
#[primary_span]
#[suggestion(style = "verbose", code = "async use", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_double_colon_in_bound)]
pub(crate) struct DoubleColonInBound {
@ -1667,6 +1688,13 @@ pub(crate) struct AsyncMoveBlockIn2015 {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_async_use_block_in_2015)]
pub(crate) struct AsyncUseBlockIn2015 {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_async_bound_modifier_in_2015)]
pub(crate) struct AsyncBoundModifierIn2015 {

View File

@ -31,15 +31,15 @@ use super::{
SeqSep, TokenType,
};
use crate::errors::{
AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, AwaitSuggestion,
BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained,
ComparisonOperatorsCannotBeChainedSugg, ConstGenericWithoutBraces,
ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType,
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,
AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
IncorrectSemicolon, IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType,
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator,
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
@ -572,10 +572,17 @@ impl<'a> Parser<'a> {
return Err(self.dcx().create_err(UseEqInstead { span: self.token.span }));
}
if self.token.is_keyword(kw::Move) && self.prev_token.is_keyword(kw::Async) {
// The 2015 edition is in use because parsing of `async move` has failed.
if (self.token.is_keyword(kw::Move) || self.token.is_keyword(kw::Use))
&& self.prev_token.is_keyword(kw::Async)
{
// The 2015 edition is in use because parsing of `async move` or `async use` has failed.
let span = self.prev_token.span.to(self.token.span);
return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span }));
if self.token.is_keyword(kw::Move) {
return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span }));
} else {
// kw::Use
return Err(self.dcx().create_err(AsyncUseBlockIn2015 { span }));
}
}
let expect = tokens_to_string(&expected);
@ -1991,7 +1998,7 @@ impl<'a> Parser<'a> {
self.parse_expr()
}
.map_err(|mut err| {
err.span_label(await_sp, "while parsing this incorrect await expression");
err.span_label(await_sp, format!("while parsing this incorrect await expression"));
err
})?;
Ok((expr.span, expr, is_question))
@ -2030,6 +2037,21 @@ impl<'a> Parser<'a> {
self.dcx().emit_err(IncorrectUseOfAwait { span });
}
}
///
/// If encountering `x.use()`, consumes and emits an error.
pub(super) fn recover_from_use(&mut self) {
if self.token == token::OpenDelim(Delimiter::Parenthesis)
&& self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
{
// var.use()
let lo = self.token.span;
self.bump(); // (
let span = lo.to(self.token.span);
self.bump(); // )
self.dcx().emit_err(IncorrectUseOfUse { span });
}
}
pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
let is_try = self.token.is_keyword(kw::Try);

View File

@ -778,6 +778,7 @@ impl<'a> Parser<'a> {
ExprKind::MethodCall(_) => "a method call",
ExprKind::Call(_, _) => "a function call",
ExprKind::Await(_, _) => "`.await`",
ExprKind::Use(_, _) => "`.use`",
ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match",
ExprKind::Err(_) => return Ok(with_postfix),
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
@ -1296,6 +1297,12 @@ impl<'a> Parser<'a> {
return Ok(self.mk_await_expr(self_arg, lo));
}
if self.eat_keyword(exp!(Use)) {
let use_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::ergonomic_clones, use_span);
return Ok(self.mk_use_expr(self_arg, lo));
}
// Post-fix match
if self.eat_keyword(exp!(Match)) {
let match_span = self.prev_token.span;
@ -1397,6 +1404,7 @@ impl<'a> Parser<'a> {
} else if this.check_path() {
this.parse_expr_path_start()
} else if this.check_keyword(exp!(Move))
|| this.check_keyword(exp!(Use))
|| this.check_keyword(exp!(Static))
|| this.check_const_closure()
{
@ -2388,7 +2396,7 @@ impl<'a> Parser<'a> {
Ok(closure)
}
/// Parses an optional `move` prefix to a closure-like construct.
/// Parses an optional `move` or `use` prefix to a closure-like construct.
fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
if self.eat_keyword(exp!(Move)) {
let move_kw_span = self.prev_token.span;
@ -2401,6 +2409,16 @@ impl<'a> Parser<'a> {
} else {
Ok(CaptureBy::Value { move_kw: move_kw_span })
}
} else if self.eat_keyword(exp!(Use)) {
let use_kw_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::ergonomic_clones, use_kw_span);
// Check for `use async` and recover
if self.check_keyword(exp!(Async)) {
let use_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
Err(self.dcx().create_err(errors::AsyncUseOrderIncorrect { span: use_async_span }))
} else {
Ok(CaptureBy::Use { use_kw: use_kw_span })
}
} else {
Ok(CaptureBy::Ref)
}
@ -3415,7 +3433,7 @@ impl<'a> Parser<'a> {
self.is_keyword_ahead(lookahead, &[kw])
&& ((
// `async move {`
self.is_keyword_ahead(lookahead + 1, &[kw::Move])
self.is_keyword_ahead(lookahead + 1, &[kw::Move, kw::Use])
&& self.look_ahead(lookahead + 2, |t| {
*t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
})
@ -3818,6 +3836,13 @@ impl<'a> Parser<'a> {
await_expr
}
fn mk_use_expr(&mut self, self_arg: P<Expr>, lo: Span) -> P<Expr> {
let span = lo.to(self.prev_token.span);
let use_expr = self.mk_expr(span, ExprKind::Use(self_arg, self.prev_token.span));
self.recover_from_use();
use_expr
}
pub(crate) fn mk_expr_with_attrs(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> {
P(Expr { kind, span, attrs, id: DUMMY_NODE_ID, tokens: None })
}
@ -3966,6 +3991,7 @@ impl MutVisitor for CondChecker<'_> {
}
ExprKind::Unary(_, _)
| ExprKind::Await(_, _)
| ExprKind::Use(_, _)
| ExprKind::AssignOp(_, _, _)
| ExprKind::Range(_, _, _)
| ExprKind::Try(_)

View File

@ -209,7 +209,7 @@ impl<'a> Parser<'a> {
let check_pub = def == &Defaultness::Final;
let mut def_ = || mem::replace(def, Defaultness::Final);
let info = if self.eat_keyword_case(exp!(Use), case) {
let info = if !self.is_use_closure() && self.eat_keyword_case(exp!(Use), case) {
self.parse_use_item()?
} else if self.check_fn_front_matter(check_pub, case) {
// FUNCTION ITEM
@ -1277,6 +1277,21 @@ impl<'a> Parser<'a> {
None
}
fn is_use_closure(&self) -> bool {
if self.token.is_keyword(kw::Use) {
// Check if this could be a closure.
self.look_ahead(1, |token| {
// Move or Async here would be an error but still we're parsing a closure
let dist =
if token.is_keyword(kw::Move) || token.is_keyword(kw::Async) { 2 } else { 1 };
self.look_ahead(dist, |token| matches!(token.kind, token::Or | token::OrOr))
})
} else {
false
}
}
fn is_unsafe_foreign_mod(&self) -> bool {
self.token.is_keyword(kw::Unsafe)
&& self.is_keyword_ahead(1, &[kw::Extern])
@ -1290,7 +1305,7 @@ impl<'a> Parser<'a> {
if self.check_keyword(exp!(Static)) {
// Check if this could be a closure.
!self.look_ahead(1, |token| {
if token.is_keyword(kw::Move) {
if token.is_keyword(kw::Move) || token.is_keyword(kw::Use) {
return true;
}
matches!(token.kind, token::Or | token::OrOr)

View File

@ -813,9 +813,9 @@ impl<'a> Parser<'a> {
self.is_keyword_ahead(0, &[kw::Const])
&& self.look_ahead(1, |t| match &t.kind {
// async closures do not work with const closures, so we do not parse that here.
token::Ident(kw::Move | kw::Static, IdentIsRaw::No) | token::OrOr | token::Or => {
true
}
token::Ident(kw::Move | kw::Use | kw::Static, IdentIsRaw::No)
| token::OrOr
| token::Or => true,
_ => false,
})
}

View File

@ -328,6 +328,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
Array,
Call,
MethodCall,
Use,
Tup,
Binary,
Unary,
@ -626,7 +627,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
(self, e, e.kind, None, ast, Expr, ExprKind),
[
Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign,
If, While, ForLoop, Loop, Match, Closure, Block, Await, Use, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy

View File

@ -426,6 +426,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
| hir::ExprKind::Array(..)
| hir::ExprKind::Call(..)
| hir::ExprKind::MethodCall(..)
| hir::ExprKind::Use(..)
| hir::ExprKind::Tup(..)
| hir::ExprKind::Binary(..)
| hir::ExprKind::AddrOf(..)
@ -705,7 +706,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
);
self.acc(self.exit_ln, var, ACC_READ | ACC_USE);
}
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
}
}
}
@ -1031,6 +1032,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(receiver, succ)
}
hir::ExprKind::Use(expr, _) => {
let succ = self.check_is_ty_uninhabited(expr, succ);
self.propagate_through_expr(expr, succ)
}
hir::ExprKind::Tup(exprs) => self.propagate_through_exprs(exprs, succ),
hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => {
@ -1418,6 +1424,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
// no correctness conditions related to liveness
hir::ExprKind::Call(..)
| hir::ExprKind::MethodCall(..)
| hir::ExprKind::Use(..)
| hir::ExprKind::Match(..)
| hir::ExprKind::Loop(..)
| hir::ExprKind::Index(..)
@ -1493,7 +1500,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
for (&var_hir_id, min_capture_list) in closure_min_captures {
for captured_place in min_capture_list {
match captured_place.info.capture_kind {
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
ty::UpvarCapture::ByRef(..) => continue,
};
let span = captured_place.get_capture_kind_span(self.ir.tcx);

View File

@ -182,6 +182,7 @@ impl CheckInlineAssembly {
| ExprKind::Array(..)
| ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Use(..)
| ExprKind::Tup(..)
| ExprKind::Binary(..)
| ExprKind::Unary(..)

View File

@ -862,6 +862,7 @@ symbols! {
eprint_macro,
eprintln_macro,
eq,
ergonomic_clones,
ermsb_target_feature,
exact_div,
except,
@ -2185,6 +2186,7 @@ symbols! {
unwrap,
unwrap_binder,
unwrap_or,
use_cloned,
use_extern_macros,
use_nested_groups,
used,

View File

@ -53,6 +53,16 @@ impl<'tcx> InferCtxt<'tcx> {
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id)
}
fn type_is_use_cloned_modulo_regions(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let ty = self.resolve_vars_if_possible(ty);
let use_cloned_def_id = self.tcx.require_lang_item(LangItem::UseCloned, None);
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, use_cloned_def_id)
}
fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)

View File

@ -14,6 +14,8 @@ ty_utils_borrow_not_supported = borrowing is not supported in generic constants
ty_utils_box_not_supported = allocations are not allowed in generic constants
ty_utils_by_use_not_supported = .use is not allowed in generic constants
ty_utils_closure_and_return_not_supported = closures and function keywords are not supported in generic constants
ty_utils_const_block_not_supported = const blocks are not supported in generic constants

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

View File

@ -230,7 +230,9 @@ fn recurse_build<'tcx>(
error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
}
ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?,
ExprKind::ByUse { .. } => {
error(GenericConstantTooComplexSub::ByUseNotSupported(node.span))?
}
ExprKind::Unary { .. } => unreachable!(),
// we handle valid unary/binary ops above
ExprKind::Binary { .. } => {
@ -317,6 +319,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::Box { .. }
| thir::ExprKind::If { .. }
| thir::ExprKind::Call { .. }
| thir::ExprKind::ByUse { .. }
| thir::ExprKind::Deref { .. }
| thir::ExprKind::Binary { .. }
| thir::ExprKind::LogicalOp { .. }

View File

@ -55,6 +55,8 @@ pub(crate) enum GenericConstantTooComplexSub {
BoxNotSupported(#[primary_span] Span),
#[label(ty_utils_binary_not_supported)]
BinaryNotSupported(#[primary_span] Span),
#[label(ty_utils_by_use_not_supported)]
ByUseNotSupported(#[primary_span] Span),
#[label(ty_utils_logical_op_not_supported)]
LogicalOpNotSupported(#[primary_span] Span),
#[label(ty_utils_assign_not_supported)]

View File

@ -56,6 +56,7 @@
//! [`Rc`]: rc
//! [`RefCell`]: core::cell
#![allow(incomplete_features)]
#![allow(unused_attributes)]
#![stable(feature = "alloc", since = "1.36.0")]
#![doc(
@ -113,6 +114,7 @@
#![feature(deprecated_suggestion)]
#![feature(deref_pure_trait)]
#![feature(dispatch_from_dyn)]
#![feature(ergonomic_clones)]
#![feature(error_generic_member_access)]
#![feature(exact_size_is_empty)]
#![feature(extend_one)]

View File

@ -245,6 +245,7 @@ use core::any::Any;
use core::cell::Cell;
#[cfg(not(no_global_oom_handling))]
use core::clone::CloneToUninit;
use core::clone::UseCloned;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::intrinsics::abort;
@ -2333,6 +2334,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Rc<T, A> {
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T: ?Sized, A: Allocator + Clone> UseCloned for Rc<T, A> {}
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Default> Default for Rc<T> {
@ -3496,6 +3500,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Weak<T, A> {
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T: ?Sized, A: Allocator + Clone> UseCloned for Weak<T, A> {}
#[stable(feature = "rc_weak", since = "1.4.0")]
impl<T: ?Sized, A: Allocator> fmt::Debug for Weak<T, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@ -11,6 +11,7 @@
use core::any::Any;
#[cfg(not(no_global_oom_handling))]
use core::clone::CloneToUninit;
use core::clone::UseCloned;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::intrinsics::abort;
@ -2197,6 +2198,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Arc<T, A> {
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T: ?Sized, A: Allocator + Clone> UseCloned for Arc<T, A> {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Arc<T, A> {
type Target = T;
@ -3158,6 +3162,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Weak<T, A> {
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T: ?Sized, A: Allocator + Clone> UseCloned for Weak<T, A> {}
#[stable(feature = "downgraded_weak", since = "1.10.0")]
impl<T> Default for Weak<T> {
/// Constructs a new `Weak<T>`, without allocating memory.

View File

@ -184,6 +184,59 @@ pub macro Clone($item:item) {
/* compiler built-in */
}
/// Trait for objects whose [`Clone`] impl is lightweight (e.g. reference-counted)
///
/// Cloning an object implementing this trait should in general:
/// - be O(1) (constant) time regardless of the amount of data managed by the object,
/// - not require a memory allocation,
/// - not require copying more than roughly 64 bytes (a typical cache line size),
/// - not block the current thread,
/// - not have any semantic side effects (e.g. allocating a file descriptor), and
/// - not have overhead larger than a couple of atomic operations.
///
/// The `UseCloned` trait does not provide a method; instead, it indicates that
/// `Clone::clone` is lightweight, and allows the use of the `.use` syntax.
///
/// ## .use postfix syntax
///
/// Values can be `.use`d by adding `.use` postfix to the value you want to use.
///
/// ```ignore (this won't work until we land use)
/// fn foo(f: Foo) {
/// // if `Foo` implements `Copy` f would be copied into x.
/// // if `Foo` implements `UseCloned` f would be cloned into x.
/// // otherwise f would be moved into x.
/// let x = f.use;
/// // ...
/// }
/// ```
///
/// ## use closures
///
/// Use closures allow captured values to be automatically used.
/// This is similar to have a closure that you would call `.use` over each captured value.
#[unstable(feature = "ergonomic_clones", issue = "132290")]
#[cfg_attr(not(bootstrap), lang = "use_cloned")]
pub trait UseCloned: Clone {
// Empty.
}
macro_rules! impl_use_cloned {
($($t:ty)*) => {
$(
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl UseCloned for $t {}
)*
}
}
impl_use_cloned! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f16 f32 f64 f128
bool char
}
// FIXME(aburka): these structs are used solely by #[derive] to
// assert that every component of a type implements Clone or Copy.
//

View File

@ -404,6 +404,8 @@ macro_rules! define_bignum {
}
}
impl crate::clone::UseCloned for $name {}
impl crate::fmt::Debug for $name {
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
let sz = if self.size < 1 { 1 } else { self.size };

View File

@ -1,6 +1,7 @@
//! Definitions of integer that is known not to equal zero.
use super::{IntErrorKind, ParseIntError};
use crate::clone::UseCloned;
use crate::cmp::Ordering;
use crate::hash::{Hash, Hasher};
use crate::marker::{Freeze, StructuralPartialEq};
@ -182,6 +183,9 @@ where
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T> UseCloned for NonZero<T> where T: ZeroablePrimitive {}
#[stable(feature = "nonzero", since = "1.28.0")]
impl<T> Copy for NonZero<T> where T: ZeroablePrimitive {}

View File

@ -2050,6 +2050,9 @@ where
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T> crate::clone::UseCloned for Option<T> where T: crate::clone::UseCloned {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Default for Option<T> {
/// Returns [`None`][Option::None].

View File

@ -1744,6 +1744,14 @@ where
}
}
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T, E> crate::clone::UseCloned for Result<T, E>
where
T: crate::clone::UseCloned,
E: crate::clone::UseCloned,
{
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, E> IntoIterator for Result<T, E> {
type Item = T;

View File

@ -2121,8 +2121,8 @@ mod unsafe_keyword {}
#[doc(keyword = "use")]
//
/// Import or rename items from other crates or modules, or specify precise capturing
/// with `use<..>`.
/// Import or rename items from other crates or modules, use values under ergonomic clones
/// semantic, or specify precise capturing with `use<..>`.
///
/// ## Importing items
///
@ -2205,6 +2205,11 @@ mod unsafe_keyword {}
///
/// For more details about precise capturing, see the [Reference][ref-impl-trait].
///
/// ## Ergonomic clones
///
/// Use a values, copying its content if the value implements `Copy`, cloning the contents if the
/// value implements `UseCloned` or moving it otherwise.
///
/// [`crate`]: keyword.crate.html
/// [`self`]: keyword.self.html
/// [`super`]: keyword.super.html

View File

@ -150,6 +150,8 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> {
}
}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
if cmt.place.projections.is_empty() {
if let PlaceBase::Local(lid) = cmt.place.base {

View File

@ -79,6 +79,8 @@ struct MutatePairDelegate<'a, 'tcx> {
impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
if bk == ty::BorrowKind::Mutable {
if let PlaceBase::Local(id) = cmt.place.base {

View File

@ -160,6 +160,7 @@ fn never_loop_expr<'tcx>(
| ExprKind::UnsafeBinderCast(_, e, _) => never_loop_expr(cx, e, local_labels, main_loop_id),
ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id),
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id),
ExprKind::Use(expr, _) => never_loop_expr(cx, expr, local_labels, main_loop_id),
ExprKind::MethodCall(_, receiver, es, _) => {
never_loop_expr_all(cx, once(receiver).chain(es.iter()), local_labels, main_loop_id)
},

View File

@ -99,7 +99,7 @@ where
});
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
match captures.get(l) {
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Value | CaptureKind::Use | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
return None;
},

View File

@ -147,6 +147,8 @@ impl<'tcx> Delegate<'tcx> for MoveDelegate {
}
}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: BorrowKind) {}
fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}

View File

@ -396,6 +396,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
}
}
fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
#[allow(clippy::if_same_then_else)]
fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) {
self.prev_bind = None;

View File

@ -326,6 +326,8 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
self.move_common(cmt);
}
fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}

View File

@ -112,6 +112,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet {
}
fn consume(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
fn use_cloned(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: HirId) {}
fn copy(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
@ -137,6 +138,7 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet {
}
fn consume(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
fn use_cloned(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: HirId) {}
fn copy(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {}

View File

@ -177,7 +177,7 @@ fn try_get_option_occurrence<'tcx>(
.then_some(())
.and_then(|()| none_captures.get(local_id))
}) {
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Value | CaptureKind::Use | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
Some(CaptureKind::Ref(Mutability::Not)) | None => (),
}

View File

@ -230,6 +230,8 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> {
fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
}

View File

@ -426,6 +426,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Tup({elements})");
self.slice(elements, |e| self.expr(e));
},
ExprKind::Use(expr, _) => {
bind!(self, expr);
kind!("Use({expr})");
self.expr(expr);
},
ExprKind::Binary(op, left, right) => {
bind!(self, op, left, right);
kind!("Binary({op}, {left}, {right})");
@ -488,6 +493,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
}) => {
let capture_clause = match capture_clause {
CaptureBy::Value { .. } => "Value { .. }",
CaptureBy::Use { .. } => "Use { .. }",
CaptureBy::Ref => "Ref",
};

View File

@ -291,6 +291,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
ExprKind::ConstBlock(_)
| ExprKind::Array(_)
| ExprKind::Tup(_)
| ExprKind::Use(..)
| ExprKind::Lit(_)
| ExprKind::Cast(..)
| ExprKind::Type(..)

View File

@ -393,6 +393,7 @@ impl HirEqInterExpr<'_, '_, '_> {
&& over(lf, rf, |l, r| self.eq_expr_field(l, r))
},
(&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
(&ExprKind::Use(l_expr, _), &ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr),
(&ExprKind::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt),
(&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
(&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re),
@ -425,6 +426,7 @@ impl HirEqInterExpr<'_, '_, '_> {
| &ExprKind::Ret(..)
| &ExprKind::Struct(..)
| &ExprKind::Tup(..)
| &ExprKind::Use(..)
| &ExprKind::Type(..)
| &ExprKind::Unary(..)
| &ExprKind::Yield(..)
@ -1053,6 +1055,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
ExprKind::Tup(tup) => {
self.hash_exprs(tup);
},
ExprKind::Use(expr, _) => {
self.hash_expr(expr);
},
ExprKind::Unary(lop, le) => {
std::mem::discriminant(&lop).hash(&mut self.s);
self.hash_expr(le);

View File

@ -1129,6 +1129,7 @@ pub fn can_move_expr_to_closure_no_visit<'tcx>(
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CaptureKind {
Value,
Use,
Ref(Mutability),
}
impl CaptureKind {
@ -1141,6 +1142,7 @@ impl std::ops::BitOr for CaptureKind {
fn bitor(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
(CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
(CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
| (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
(CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
@ -1220,7 +1222,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
},
ExprKind::Let(let_expr) => {
let mutability = match pat_capture_kind(cx, let_expr.pat) {
CaptureKind::Value => Mutability::Not,
CaptureKind::Value | CaptureKind::Use => Mutability::Not,
CaptureKind::Ref(m) => m,
};
return CaptureKind::Ref(mutability);
@ -1229,7 +1231,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
let mut mutability = Mutability::Not;
for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
match capture {
CaptureKind::Value => break,
CaptureKind::Value | CaptureKind::Use => break,
CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
CaptureKind::Ref(Mutability::Not) => (),
}
@ -1239,7 +1241,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
_ => break,
},
Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
CaptureKind::Value => break,
CaptureKind::Value | CaptureKind::Use => break,
capture @ CaptureKind::Ref(_) => return capture,
},
_ => break,
@ -1294,6 +1296,7 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'
if !self.locals.contains(&local_id) {
let capture = match capture.info.capture_kind {
UpvarCapture::ByValue => CaptureKind::Value,
UpvarCapture::ByUse => CaptureKind::Use,
UpvarCapture::ByRef(kind) => match kind {
BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
BorrowKind::UniqueImmutable | BorrowKind::Mutable => {

View File

@ -147,6 +147,7 @@ impl<'a> Sugg<'a> {
| ExprKind::Become(..)
| ExprKind::Struct(..)
| ExprKind::Tup(..)
| ExprKind::Use(..)
| ExprKind::Err(_)
| ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)),
ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet),
@ -217,6 +218,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Try(..)
| ast::ExprKind::TryBlock(..)
| ast::ExprKind::Tup(..)
| ast::ExprKind::Use(..)
| ast::ExprKind::Array(..)
| ast::ExprKind::While(..)
| ast::ExprKind::Await(..)
@ -835,6 +837,8 @@ impl<'tcx> DerefDelegate<'_, 'tcx> {
impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
if let PlaceBase::Local(id) = cmt.place.base {
let map = self.cx.tcx.hir();

View File

@ -66,6 +66,8 @@ impl MutVarsDelegate {
impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
if bk == ty::BorrowKind::Mutable {
self.update(cmt);

View File

@ -648,6 +648,9 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
helper(typeck, true, arg, f)?;
}
},
ExprKind::Use(expr, _) => {
helper(typeck, true, expr, f)?;
},
ExprKind::Index(borrowed, consumed, _)
| ExprKind::Assign(borrowed, consumed, _)
| ExprKind::AssignOp(_, borrowed, consumed) => {

View File

@ -137,6 +137,10 @@ pub(crate) fn format_expr(
ast::ExprKind::Tup(ref items) => {
rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1)
}
ast::ExprKind::Use(_, _) => {
// FIXME: properly implement this
Ok(context.snippet(expr.span()).to_owned())
}
ast::ExprKind::Let(ref pat, ref expr, _span, _) => rewrite_let(context, shape, pat, expr),
ast::ExprKind::If(..)
| ast::ExprKind::ForLoop { .. }

View File

@ -513,6 +513,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::Become(..)
| ast::ExprKind::Yeet(..)
| ast::ExprKind::Tup(..)
| ast::ExprKind::Use(..)
| ast::ExprKind::Type(..)
| ast::ExprKind::Yield(None)
| ast::ExprKind::Underscore => false,

View File

@ -44,22 +44,22 @@ note: while trying to match `r#async`
LL | (r#async) => (1)
| ^^^^^^^
error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||`
--> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23
|
LL | ($i: ident) => ($i)
| ^ expected one of `move`, `|`, or `||`
| ^ expected one of `move`, `use`, `|`, or `||`
|
::: $DIR/edition-keywords-2018-2015-parsing.rs:22:8
|
LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved
| -------------------- in this macro invocation
error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||`
--> $DIR/edition-keywords-2018-2015-parsing.rs:24:24
|
LL | if passes_tt!(async) == 1 {}
| ^ expected one of `move`, `|`, or `||`
| ^ expected one of `move`, `use`, `|`, or `||`
error[E0308]: mismatched types
--> $DIR/edition-keywords-2018-2015-parsing.rs:29:33

View File

@ -44,34 +44,34 @@ note: while trying to match `r#async`
LL | (r#async) => (1)
| ^^^^^^^
error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||`
--> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23
|
LL | ($i: ident) => ($i)
| ^ expected one of `move`, `|`, or `||`
| ^ expected one of `move`, `use`, `|`, or `||`
|
::: $DIR/edition-keywords-2018-2018-parsing.rs:29:8
|
LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved
| -------------------- in this macro invocation
error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||`
--> $DIR/edition-keywords-2018-2018-parsing.rs:31:24
|
LL | if passes_tt!(async) == 1 {}
| ^ expected one of `move`, `|`, or `||`
| ^ expected one of `move`, `use`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||`
--> $DIR/edition-keywords-2018-2018-parsing.rs:14:23
|
LL | ($i: ident) => ($i)
| ^ expected one of `move`, `|`, or `||`
| ^ expected one of `move`, `use`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||`
--> $DIR/edition-keywords-2018-2018-parsing.rs:35:30
|
LL | if local_passes_tt!(async) == 1 {}
| ^ expected one of `move`, `|`, or `||`
| ^ expected one of `move`, `use`, `|`, or `||`
error[E0308]: mismatched types
--> $DIR/edition-keywords-2018-2018-parsing.rs:40:33

View File

@ -0,0 +1,17 @@
//@ check-pass
//@ edition:2018
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
use std::future::Future;
fn ergonomic_clone_async_closures() -> impl Future<Output = String> {
let s = String::from("hi");
async use {
s
}
}
fn main() {}

View File

@ -0,0 +1,7 @@
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn main() {
async use {};
//~^ ERROR `async use` blocks are only allowed in Rust 2018 or later
}

View File

@ -0,0 +1,8 @@
error: `async use` blocks are only allowed in Rust 2018 or later
--> $DIR/edition-2015.rs:5:5
|
LL | async use {};
| ^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,10 @@
// Check that using the parameter name in its type does not ICE.
//@ edition:2018
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn main() {
let _ = async use |x: x| x; //~ ERROR expected type
let _ = async use |x: bool| -> x { x }; //~ ERROR expected type
}

View File

@ -0,0 +1,15 @@
error[E0573]: expected type, found local variable `x`
--> $DIR/local-type.rs:8:27
|
LL | let _ = async use |x: x| x;
| ^ not a type
error[E0573]: expected type, found local variable `x`
--> $DIR/local-type.rs:9:36
|
LL | let _ = async use |x: bool| -> x { x };
| ^ not a type
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0573`.

View File

@ -0,0 +1,58 @@
//@ check-pass
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
use std::clone::UseCloned;
use std::future::Future;
fn ergonomic_clone_closure_no_captures() -> i32 {
let cl = use || {
1
};
cl()
}
fn ergonomic_clone_closure_move() -> String {
let s = String::from("hi");
let cl = use || {
s
};
cl()
}
#[derive(Clone)]
struct Foo;
impl UseCloned for Foo {}
fn ergonomic_clone_closure_use_cloned() -> Foo {
let f = Foo;
let f1 = use || {
f
};
let f2 = use || {
f
};
f
}
fn ergonomic_clone_closure_copy() -> i32 {
let i = 1;
let i1 = use || {
i
};
let i2 = use || {
i
};
i
}
fn main() {}

View File

@ -0,0 +1,11 @@
//@ check-pass
#![feature(const_closures)]
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
const fn foo() {
let cl = const use || {};
}
fn main() {}

View File

@ -0,0 +1,25 @@
#![feature(ergonomic_clones)]
#![allow(warnings)]
fn closure_expecting_bound<F>(_: F)
where
F: FnOnce(&u32),
{
}
fn expect_bound_supply_named<'x>() {
let mut f: Option<&u32> = None;
// Here we give a type annotation that `x` should be free. We get
// an error because of that.
closure_expecting_bound(use |x: &'x u32| {
//~^ ERROR lifetime may not live long enough
//~| ERROR lifetime may not live long enough
// Borrowck doesn't get a chance to run, but if it did it should error
// here.
f = Some(x);
});
}
fn main() {}

View File

@ -0,0 +1,22 @@
error: lifetime may not live long enough
--> $DIR/expect-region.rs:15:34
|
LL | fn expect_bound_supply_named<'x>() {
| -- lifetime `'x` defined here
...
LL | closure_expecting_bound(use |x: &'x u32| {
| ^ - let's call the lifetime of this reference `'1`
| |
| requires that `'1` must outlive `'x`
error: lifetime may not live long enough
--> $DIR/expect-region.rs:15:34
|
LL | fn expect_bound_supply_named<'x>() {
| -- lifetime `'x` defined here
...
LL | closure_expecting_bound(use |x: &'x u32| {
| ^ requires that `'x` must outlive `'static`
error: aborting due to 2 previous errors

View File

@ -0,0 +1,14 @@
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn get_closure() -> Box<dyn Fn() -> Vec<u8>> {
let vec = vec![1u8, 2u8];
let closure = use || { //~ ERROR expected a closure
vec
};
Box::new(closure)
}
fn main() {}

View File

@ -0,0 +1,16 @@
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> $DIR/fn-once.rs:7:19
|
LL | let closure = use || {
| ^^^^^^ this closure implements `FnOnce`, not `Fn`
LL | vec
| --- closure is `FnOnce` because it moves the variable `vec` out of its environment
...
LL | Box::new(closure)
| ----------------- the requirement to implement `Fn` derives from here
|
= note: required for the cast from `Box<{closure@$DIR/fn-once.rs:7:19: 7:25}>` to `Box<(dyn Fn() -> Vec<u8> + 'static)>`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0525`.

View File

@ -0,0 +1,16 @@
//@ run-rustfix
// Point at the captured immutable outer variable
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn foo(mut f: Box<dyn FnMut()>) {
f();
}
fn main() {
let mut y = true;
foo(Box::new(use || y = !y) as Box<_>);
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
}

View File

@ -0,0 +1,16 @@
//@ run-rustfix
// Point at the captured immutable outer variable
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn foo(mut f: Box<dyn FnMut()>) {
f();
}
fn main() {
let y = true;
foo(Box::new(use || y = !y) as Box<_>);
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
}

View File

@ -0,0 +1,14 @@
error[E0594]: cannot assign to `y`, as it is not declared as mutable
--> $DIR/immutable-outer-variable.rs:14:25
|
LL | foo(Box::new(use || y = !y) as Box<_>);
| ^^^^^^ cannot assign
|
help: consider changing this to be mutable
|
LL | let mut y = true;
| +++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0594`.

View File

@ -0,0 +1,9 @@
// Check that using the parameter name in its type does not ICE.
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn main() {
let _ = use |x: x| x; //~ ERROR expected type
let _ = use |x: bool| -> x { x }; //~ ERROR expected type
}

View File

@ -0,0 +1,15 @@
error[E0573]: expected type, found local variable `x`
--> $DIR/local-type.rs:7:21
|
LL | let _ = use |x: x| x;
| ^ not a type
error[E0573]: expected type, found local variable `x`
--> $DIR/local-type.rs:8:30
|
LL | let _ = use |x: bool| -> x { x };
| ^ not a type
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0573`.

View File

@ -0,0 +1,12 @@
//@ check-pass
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn main() {
let mut my_var = false;
let mut callback = use || {
my_var = true;
};
callback();
}

View File

@ -0,0 +1,11 @@
#![feature(ergonomic_clones)]
#![allow(incomplete_features)]
fn main() {
let mut my_var = false;
let callback = use || {
my_var = true;
};
callback();
//~^ ERROR cannot borrow `callback` as mutable, as it is not declared as mutable [E0596]
}

View File

@ -0,0 +1,17 @@
error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
--> $DIR/mutation2.rs:9:5
|
LL | my_var = true;
| ------ calling `callback` requires mutable binding due to possible mutation of `my_var`
LL | };
LL | callback();
| ^^^^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
LL | let mut callback = use || {
| +++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0596`.

Some files were not shown because too many files have changed in this diff Show More