mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-13 04:26:48 +00:00
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:
commit
f5a143f796
@ -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 ()|`.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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 => {}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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"),
|
||||
|
@ -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),
|
||||
|
@ -297,6 +297,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||
| ExprKind::AssignOp(_, _, _)
|
||||
| ExprKind::Gen(_, _, _, _)
|
||||
| ExprKind::Await(_, _)
|
||||
| ExprKind::Use(_, _)
|
||||
| ExprKind::Block(_, _)
|
||||
| ExprKind::Break(_, _)
|
||||
| ExprKind::Closure(_)
|
||||
|
@ -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.
|
||||
|
@ -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`).
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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 => {}
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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 { .. }
|
||||
|
@ -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);
|
||||
|
@ -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>> {
|
||||
|
@ -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,
|
||||
|
@ -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 }
|
||||
|
@ -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,
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)));
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@ impl Category {
|
||||
| ExprKind::RawBorrow { .. }
|
||||
| ExprKind::Yield { .. }
|
||||
| ExprKind::Call { .. }
|
||||
| ExprKind::ByUse { .. }
|
||||
| ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
|
||||
|
||||
ExprKind::Array { .. }
|
||||
|
@ -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`
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 { .. }
|
||||
|
@ -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,
|
||||
|
@ -345,6 +345,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||
| Borrow { .. }
|
||||
| Box { .. }
|
||||
| Call { .. }
|
||||
| ByUse { .. }
|
||||
| Closure { .. }
|
||||
| ConstBlock { .. }
|
||||
| ConstParam { .. }
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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(_)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -182,6 +182,7 @@ impl CheckInlineAssembly {
|
||||
| ExprKind::Array(..)
|
||||
| ExprKind::Call(..)
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Use(..)
|
||||
| ExprKind::Tup(..)
|
||||
| ExprKind::Binary(..)
|
||||
| ExprKind::Unary(..)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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 { .. }
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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 };
|
||||
|
@ -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 {}
|
||||
|
||||
|
@ -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].
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
},
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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) {}
|
||||
|
@ -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;
|
||||
|
@ -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) {}
|
||||
|
@ -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) {}
|
||||
|
@ -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 => (),
|
||||
}
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
};
|
||||
|
||||
|
@ -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(..)
|
||||
|
@ -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);
|
||||
|
@ -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 => {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) => {
|
||||
|
@ -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 { .. }
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
17
tests/ui/ergonomic-clones/async/basic.rs
Normal file
17
tests/ui/ergonomic-clones/async/basic.rs
Normal 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() {}
|
7
tests/ui/ergonomic-clones/async/edition-2015.rs
Normal file
7
tests/ui/ergonomic-clones/async/edition-2015.rs
Normal 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
|
||||
}
|
8
tests/ui/ergonomic-clones/async/edition-2015.stderr
Normal file
8
tests/ui/ergonomic-clones/async/edition-2015.stderr
Normal 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
|
||||
|
10
tests/ui/ergonomic-clones/async/local-type.rs
Normal file
10
tests/ui/ergonomic-clones/async/local-type.rs
Normal 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
|
||||
}
|
15
tests/ui/ergonomic-clones/async/local-type.stderr
Normal file
15
tests/ui/ergonomic-clones/async/local-type.stderr
Normal 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`.
|
58
tests/ui/ergonomic-clones/closure/basic.rs
Normal file
58
tests/ui/ergonomic-clones/closure/basic.rs
Normal 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() {}
|
11
tests/ui/ergonomic-clones/closure/const-closure.rs
Normal file
11
tests/ui/ergonomic-clones/closure/const-closure.rs
Normal 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() {}
|
25
tests/ui/ergonomic-clones/closure/expect-region.rs
Normal file
25
tests/ui/ergonomic-clones/closure/expect-region.rs
Normal 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() {}
|
22
tests/ui/ergonomic-clones/closure/expect-region.stderr
Normal file
22
tests/ui/ergonomic-clones/closure/expect-region.stderr
Normal 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
|
||||
|
14
tests/ui/ergonomic-clones/closure/fn-once.rs
Normal file
14
tests/ui/ergonomic-clones/closure/fn-once.rs
Normal 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() {}
|
16
tests/ui/ergonomic-clones/closure/fn-once.stderr
Normal file
16
tests/ui/ergonomic-clones/closure/fn-once.stderr
Normal 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`.
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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`.
|
9
tests/ui/ergonomic-clones/closure/local-type.rs
Normal file
9
tests/ui/ergonomic-clones/closure/local-type.rs
Normal 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
|
||||
}
|
15
tests/ui/ergonomic-clones/closure/local-type.stderr
Normal file
15
tests/ui/ergonomic-clones/closure/local-type.stderr
Normal 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`.
|
12
tests/ui/ergonomic-clones/closure/mutation.rs
Normal file
12
tests/ui/ergonomic-clones/closure/mutation.rs
Normal 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();
|
||||
}
|
11
tests/ui/ergonomic-clones/closure/mutation2.rs
Normal file
11
tests/ui/ergonomic-clones/closure/mutation2.rs
Normal 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]
|
||||
}
|
17
tests/ui/ergonomic-clones/closure/mutation2.stderr
Normal file
17
tests/ui/ergonomic-clones/closure/mutation2.stderr
Normal 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
Loading…
Reference in New Issue
Block a user