Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ergonomic ref counting #134797

Merged
merged 25 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0cf8dbc
Add ergonomic_clones feature flag
spastorino Oct 29, 2024
05c5164
Implement .use keyword as an alias of clone
spastorino Oct 2, 2024
81a926c
Use closure parse code
spastorino Nov 1, 2024
57cb498
Generate the right MIR for by use closures
spastorino Dec 17, 2024
dcdfd55
Add UseCloned trait related code
spastorino Dec 11, 2024
60b6470
Fix clippy
spastorino Dec 26, 2024
8c456cb
Fix rustfmt
spastorino Dec 27, 2024
4cbca77
Separate closures, async and dotuse tests in directories
spastorino Dec 27, 2024
38b4746
Support nested use closures
spastorino Feb 10, 2025
7c17bf8
Add tests
spastorino Feb 10, 2025
292aa87
Fix use closure parsing error message
spastorino Feb 11, 2025
6eb6ff6
Allow to mutate use captures
spastorino Feb 14, 2025
aa58439
Fail gracefully if mutating on a use closure and the closure it not d…
spastorino Feb 14, 2025
18d689c
Add more tests
spastorino Feb 14, 2025
edcbc9b
Add a code example as comment in init_capture_kind_for_place
spastorino Feb 20, 2025
b43b700
Account for UseCloned on expr_use_visitor
spastorino Feb 20, 2025
2f48fce
Change feature flag error to be ergonomic clones are experimental
spastorino Feb 21, 2025
4e6407a
Give a better error message on async use in edition 2015
spastorino Feb 21, 2025
65d65e5
Parse and allow const use closures
spastorino Feb 21, 2025
1702c00
Make captures in ByUse context be always ty::UpvarCapture::ByUse
spastorino Feb 21, 2025
a68db7e
Add examples in stdlib demonstrating the use syntax
spastorino Feb 24, 2025
42b8b13
Add some code comments
spastorino Mar 6, 2025
5a6d00c
Add allow(incomplete_features) to alloc
spastorino Mar 6, 2025
d7104dc
Make feature flag incomplete
spastorino Mar 6, 2025
d2bde63
Add slight variation to feature-gate ergonomic clones test
spastorino Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,7 @@ impl Expr {
// Never need parens
ExprKind::Array(_)
| ExprKind::Await(..)
| ExprKind::Use(..)
| ExprKind::Block(..)
| ExprKind::Call(..)
| ExprKind::ConstBlock(_)
Expand Down Expand Up @@ -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>),
Expand Down Expand Up @@ -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 ()|`.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, _)
Expand Down Expand Up @@ -224,6 +225,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Lit(_)
| Type(_, _)
| Await(_, _)
| Use(_, _)
| Field(_, _)
| Index(_, _, _)
| Underscore
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/borrowck_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
20 changes: 13 additions & 7 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::AssignOp(_, _, _)
| ExprKind::Gen(_, _, _, _)
| ExprKind::Await(_, _)
| ExprKind::Use(_, _)
| ExprKind::Block(_, _)
| ExprKind::Break(_, _)
| ExprKind::Closure(_)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,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.
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,7 @@ impl Expr<'_> {
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Use(..)
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,

ExprKind::DropTemps(expr, ..) => expr.precedence(),
Expand Down Expand Up @@ -2212,6 +2213,7 @@ impl Expr<'_> {
ExprKind::Path(QPath::TypeRelative(..))
| ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Use(..)
| ExprKind::Struct(..)
| ExprKind::Tup(..)
| ExprKind::If(..)
Expand Down Expand Up @@ -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, _)
Expand Down Expand Up @@ -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`).
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,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);
}
Expand Down Expand Up @@ -2301,6 +2305,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 => {}
}
}
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_, _, _)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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>,
Expand Down
Loading
Loading