Skip to content

Commit

Permalink
Rollup merge of rust-lang#134797 - spastorino:ergonomic-ref-counting-…
Browse files Browse the repository at this point in the history
…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: rust-lang/rfcs#3680
Tracking issue: rust-lang#132290
Project goal: rust-lang/rust-project-goals#107

r? ``@nikomatsakis``
  • Loading branch information
matthiaskrgr authored Mar 7, 2025
2 parents 03eb454 + d2bde63 commit 9bdbb35
Show file tree
Hide file tree
Showing 119 changed files with 1,401 additions and 79 deletions.
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 @@ -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.
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 @@ -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);
}
Expand Down Expand Up @@ -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 => {}
}
}
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

0 comments on commit 9bdbb35

Please sign in to comment.