1
Fork 0

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

Ergonomic ref counting

This is an experimental first version of ergonomic ref counting.

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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