From 5f8954bc4185bf825eb84a44d3833e0ce78590da Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 16 May 2025 12:38:51 +0200 Subject: Fix the issue of typo of comma in arm parsing --- compiler/rustc_parse/src/parser/expr.rs | 54 ++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 18 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2a7910a6af4..1a44f4af8a6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3311,26 +3311,44 @@ impl<'a> Parser<'a> { let sm = this.psess.source_map(); if let Ok(expr_lines) = sm.span_to_lines(expr_span) && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span) - && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col && expr_lines.lines.len() == 2 { - // We check whether there's any trailing code in the parse span, - // if there isn't, we very likely have the following: - // - // X | &Y => "y" - // | -- - missing comma - // | | - // | arrow_span - // X | &X => "x" - // | - ^^ self.token.span - // | | - // | parsed until here as `"y" & X` - err.span_suggestion_short( - arm_start_span.shrink_to_hi(), - "missing a comma here to end this `match` arm", - ",", - Applicability::MachineApplicable, - ); + if arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col { + // We check whether there's any trailing code in the parse span, + // if there isn't, we very likely have the following: + // + // X | &Y => "y" + // | -- - missing comma + // | | + // | arrow_span + // X | &X => "x" + // | - ^^ self.token.span + // | | + // | parsed until here as `"y" & X` + err.span_suggestion_short( + arm_start_span.shrink_to_hi(), + "missing a comma here to end this `match` arm", + ",", + Applicability::MachineApplicable, + ); + } else if arm_start_lines.lines[0].end_col + rustc_span::CharPos(1) + == expr_lines.lines[0].end_col + { + // similar to the above, but we may typo a `.` or `/` at the end of the line + let comma_span = arm_start_span + .shrink_to_hi() + .with_hi(arm_start_span.hi() + rustc_span::BytePos(1)); + if let Ok(res) = sm.span_to_snippet(comma_span) + && (res == "." || res == "/") + { + err.span_suggestion_short( + comma_span, + "you might have meant to write a `,` to end this `match` arm", + ",", + Applicability::MachineApplicable, + ); + } + } } } else { err.span_label( -- cgit 1.4.1-3-g733a5 From d3347bb32b01e73db96d75579ec7ca16320d4317 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 26 May 2025 14:11:02 +0800 Subject: remove eq_unspanned from TokenStream --- compiler/rustc_ast/src/tokenstream.rs | 10 +++------- compiler/rustc_parse/src/parser/tokenstream/tests.rs | 8 ++++++-- src/tools/clippy/clippy_utils/src/ast_utils/mod.rs | 4 +++- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 7f2ba28f81b..3c231be20dc 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -57,7 +57,9 @@ impl TokenTree { match (self, other) { (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind, (TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => { - delim == delim2 && tts.eq_unspanned(tts2) + delim == delim2 + && tts.len() == tts2.len() + && tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b)) } _ => false, } @@ -694,12 +696,6 @@ impl TokenStream { TokenStreamIter::new(self) } - /// Compares two `TokenStream`s, checking equality without regarding span information. - pub fn eq_unspanned(&self, other: &TokenStream) -> bool { - self.len() == other.len() - && self.iter().zip(other.iter()).all(|(tt1, tt2)| tt1.eq_unspanned(tt2)) - } - /// Create a token stream containing a single token with alone spacing. The /// spacing used for the final token in a constructed stream doesn't matter /// because it's never used. In practice we arbitrarily use diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index aac75323ff3..19b2c98f5af 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -14,6 +14,10 @@ fn sp(a: u32, b: u32) -> Span { Span::with_root_ctxt(BytePos(a), BytePos(b)) } +fn cmp_token_stream(a: &TokenStream, b: &TokenStream) -> bool { + a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.eq_unspanned(y)) +} + #[test] fn test_concat() { create_default_session_globals_then(|| { @@ -25,7 +29,7 @@ fn test_concat() { eq_res.push_stream(test_snd); assert_eq!(test_res.iter().count(), 5); assert_eq!(eq_res.iter().count(), 5); - assert_eq!(test_res.eq_unspanned(&eq_res), true); + assert_eq!(cmp_token_stream(&test_res, &eq_res), true); }) } @@ -104,7 +108,7 @@ fn test_dotdotdot() { stream.push_tree(TokenTree::token_joint(token::Dot, sp(0, 1))); stream.push_tree(TokenTree::token_joint(token::Dot, sp(1, 2))); stream.push_tree(TokenTree::token_alone(token::Dot, sp(2, 3))); - assert!(stream.eq_unspanned(&string_to_ts("..."))); + assert!(cmp_token_stream(&stream, &string_to_ts("..."))); assert_eq!(stream.iter().count(), 1); }) } diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 8996b694ed8..01e916fb535 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -960,5 +960,7 @@ pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { } pub fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool { - l.delim == r.delim && l.tokens.eq_unspanned(&r.tokens) + l.delim == r.delim + && l.tokens.len() == r.tokens.len() + && l.tokens.iter().zip(r.tokens.iter()).all(|(a, b)| a.eq_unspanned(b)) } -- cgit 1.4.1-3-g733a5 From 0f285e346f0432b08d82e85fc51419994b76504b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 27 May 2025 02:14:33 +1000 Subject: Remove the one use of `P<[T]>`. A `Vec` is fine, the additional word (vector vs. boxed slice) doesn't matter here. --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index c4be07ba068..4c821c00ad6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1714,7 +1714,7 @@ pub enum ExprKind { /// /// Usually not written directly in user code but /// indirectly via the macro `core::mem::offset_of!(...)`. - OffsetOf(P, P<[Ident]>), + OffsetOf(P, Vec), /// A macro invocation; pre-expansion. MacCall(P), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2a7910a6af4..6e134bad7bd 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1119,7 +1119,7 @@ impl<'a> Parser<'a> { /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. - fn parse_floating_field_access(&mut self) -> PResult<'a, P<[Ident]>> { + fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { let mut fields = Vec::new(); let mut trailing_dot = None; -- cgit 1.4.1-3-g733a5 From 991c91fdaa5862835799abfd9c2349e06277097e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 27 May 2025 02:32:22 +1000 Subject: Reduce `P` to a typedef of `Box`. Keep the `P` constructor function for now, to minimize immediate churn. All the `into_inner` calls are removed, which is nice. --- compiler/rustc_ast/src/attr/mod.rs | 2 +- compiler/rustc_ast/src/ptr.rs | 80 ++-------------------- compiler/rustc_builtin_macros/src/deriving/mod.rs | 2 +- compiler/rustc_builtin_macros/src/pattern_type.rs | 20 +++--- compiler/rustc_builtin_macros/src/test.rs | 8 +-- compiler/rustc_expand/src/base.rs | 2 +- compiler/rustc_expand/src/expand.rs | 37 +++++----- compiler/rustc_parse/src/parser/diagnostics.rs | 4 +- compiler/rustc_parse/src/parser/expr.rs | 6 +- compiler/rustc_parse/src/parser/item.rs | 4 +- compiler/rustc_parse/src/parser/pat.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 6 +- .../clippy_lints/src/unnested_or_patterns.rs | 2 +- src/tools/rustfmt/src/modules.rs | 2 +- src/tools/rustfmt/src/parse/macros/cfg_if.rs | 2 +- 15 files changed, 48 insertions(+), 131 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index f165c4ddcdd..621e3042b62 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -63,7 +63,7 @@ impl Attribute { pub fn unwrap_normal_item(self) -> AttrItem { match self.kind { - AttrKind::Normal(normal) => normal.into_inner().item, + AttrKind::Normal(normal) => normal.item, AttrKind::DocComment(..) => panic!("unexpected doc comment"), } } diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index 91f29a4c289..fffeab8bbca 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -1,81 +1,11 @@ -//! The AST pointer. -//! -//! Provides [`P`][struct@P], an owned smart pointer. -//! -//! # Motivations and benefits -//! -//! * **Identity**: sharing AST nodes is problematic for the various analysis -//! passes (e.g., one may be able to bypass the borrow checker with a shared -//! `ExprKind::AddrOf` node taking a mutable borrow). -//! -//! * **Efficiency**: folding can reuse allocation space for `P` and `Vec`, -//! the latter even when the input and output types differ (as it would be the -//! case with arenas or a GADT AST using type parameters to toggle features). -//! -//! * **Maintainability**: `P` provides an interface, which can remain fully -//! functional even if the implementation changes (using a special thread-local -//! heap, for example). Moreover, a switch to, e.g., `P<'a, T>` would be easy -//! and mostly automated. - -use std::fmt::{self, Debug}; -use std::ops::{Deref, DerefMut}; - -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - -/// An owned smart pointer. +/// A pointer type that uniquely owns a heap allocation of type T. /// -/// See the [module level documentation][crate::ptr] for details. -pub struct P { - ptr: Box, -} +/// This used to be its own type, but now it's just a typedef for `Box` and we are planning to +/// remove it soon. +pub type P = Box; /// Construct a `P` from a `T` value. #[allow(non_snake_case)] pub fn P(value: T) -> P { - P { ptr: Box::new(value) } -} - -impl P { - /// Consume the `P` and return the wrapped value. - pub fn into_inner(self) -> T { - *self.ptr - } -} - -impl Deref for P { - type Target = T; - - fn deref(&self) -> &T { - &self.ptr - } -} - -impl DerefMut for P { - fn deref_mut(&mut self) -> &mut T { - &mut self.ptr - } -} - -impl Clone for P { - fn clone(&self) -> P { - P((**self).clone()) - } -} - -impl Debug for P { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Debug::fmt(&self.ptr, f) - } -} - -impl> Decodable for P { - fn decode(d: &mut D) -> P { - P(Decodable::decode(d)) - } -} - -impl> Encodable for P { - fn encode(&self, s: &mut S) { - (**self).encode(s); - } + Box::new(value) } diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 50e7b989ed8..e45d09b5796 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -57,7 +57,7 @@ impl MultiItemModifier for BuiltinDerive { let mut items = Vec::new(); match item { Annotatable::Stmt(stmt) => { - if let ast::StmtKind::Item(item) = stmt.into_inner().kind { + if let ast::StmtKind::Item(item) = stmt.kind { (self.0)( ecx, span, diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 3529e5525fc..b61af0b0aaa 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -30,14 +30,12 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P let pat = pat_to_ty_pat( cx, - parser - .parse_pat_no_top_guard( - None, - RecoverComma::No, - RecoverColon::No, - CommaRecoveryMode::EitherTupleOrPipe, - )? - .into_inner(), + *parser.parse_pat_no_top_guard( + None, + RecoverComma::No, + RecoverColon::No, + CommaRecoveryMode::EitherTupleOrPipe, + )?, ); if parser.token != token::Eof { @@ -58,9 +56,9 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> P { end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })), include_end, ), - ast::PatKind::Or(variants) => TyPatKind::Or( - variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat.into_inner())).collect(), - ), + ast::PatKind::Or(variants) => { + TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, *pat)).collect()) + } ast::PatKind::Err(guar) => TyPatKind::Err(guar), _ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")), }; diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index fbe91531e5a..b439fa34f5b 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -40,7 +40,7 @@ pub(crate) fn expand_test_case( let (mut item, is_stmt) = match anno_item { Annotatable::Item(item) => (item, false), Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => { - if let ast::StmtKind::Item(i) = stmt.into_inner().kind { + if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() @@ -120,11 +120,7 @@ pub(crate) fn expand_test_or_bench( Annotatable::Item(i) => (i, false), Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => { // FIXME: Use an 'if let' guard once they are implemented - if let ast::StmtKind::Item(i) = stmt.into_inner().kind { - (i, true) - } else { - unreachable!() - } + if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() } } other => { not_testable_error(cx, attr_sp, None); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 55751aa4908..058e45cdc4c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -167,7 +167,7 @@ impl Annotatable { pub fn expect_stmt(self) -> ast::Stmt { match self { - Annotatable::Stmt(stmt) => stmt.into_inner(), + Annotatable::Stmt(stmt) => *stmt, _ => panic!("expected statement"), } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 81d4d59ee04..4c7bc912f8f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1188,9 +1188,8 @@ impl InvocationCollectorNode for P { matches!(self.kind, ItemKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.into_inner(); - match node.kind { - ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + match self.kind { + ItemKind::MacCall(mac) => (mac, self.attrs, AddSemicolon::No), _ => unreachable!(), } } @@ -1344,7 +1343,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTag> matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let item = self.wrapped.into_inner(); + let item = self.wrapped; match item.kind { AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), _ => unreachable!(), @@ -1385,7 +1384,7 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let item = self.wrapped.into_inner(); + let item = self.wrapped; match item.kind { AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), _ => unreachable!(), @@ -1426,7 +1425,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitImplItem matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let item = self.wrapped.into_inner(); + let item = self.wrapped; match item.kind { AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), _ => unreachable!(), @@ -1464,9 +1463,8 @@ impl InvocationCollectorNode for P { matches!(self.kind, ForeignItemKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.into_inner(); - match node.kind { - ForeignItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + match self.kind { + ForeignItemKind::MacCall(mac) => (mac, self.attrs, AddSemicolon::No), _ => unreachable!(), } } @@ -1601,16 +1599,16 @@ impl InvocationCollectorNode for ast::Stmt { // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. let (add_semicolon, mac, attrs) = match self.kind { StmtKind::MacCall(mac) => { - let ast::MacCallStmt { mac, style, attrs, .. } = mac.into_inner(); + let ast::MacCallStmt { mac, style, attrs, .. } = *mac; (style == MacStmtStyle::Semicolon, mac, attrs) } - StmtKind::Item(item) => match item.into_inner() { + StmtKind::Item(item) => match *item { ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { (mac.args.need_semicolon(), mac, attrs) } _ => unreachable!(), }, - StmtKind::Semi(expr) => match expr.into_inner() { + StmtKind::Semi(expr) => match *expr { ast::Expr { kind: ExprKind::MacCall(mac), attrs, .. } => { (mac.args.need_semicolon(), mac, attrs) } @@ -1691,8 +1689,7 @@ impl InvocationCollectorNode for P { matches!(self.kind, ast::TyKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.into_inner(); - match node.kind { + match self.kind { TyKind::MacCall(mac) => (mac, AttrVec::new(), AddSemicolon::No), _ => unreachable!(), } @@ -1715,8 +1712,7 @@ impl InvocationCollectorNode for P { matches!(self.kind, PatKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.into_inner(); - match node.kind { + match self.kind { PatKind::MacCall(mac) => (mac, AttrVec::new(), AddSemicolon::No), _ => unreachable!(), } @@ -1742,9 +1738,8 @@ impl InvocationCollectorNode for P { matches!(self.kind, ExprKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.into_inner(); - match node.kind { - ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + match self.kind { + ExprKind::MacCall(mac) => (mac, self.attrs, AddSemicolon::No), _ => unreachable!(), } } @@ -1768,7 +1763,7 @@ impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.wrapped.into_inner(); + let node = self.wrapped; match node.kind { ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), _ => unreachable!(), @@ -1806,7 +1801,7 @@ impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) } fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { - let node = self.wrapped.into_inner(); + let node = self.wrapped; match node.kind { ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), _ => unreachable!(), diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 6277dde7c97..b49a13ce584 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2273,9 +2273,9 @@ impl<'a> Parser<'a> { ), // Also catches `fn foo(&a)`. PatKind::Ref(ref inner_pat, mutab) - if matches!(inner_pat.clone().into_inner().kind, PatKind::Ident(..)) => + if matches!(inner_pat.clone().kind, PatKind::Ident(..)) => { - match inner_pat.clone().into_inner().kind { + match inner_pat.clone().kind { PatKind::Ident(_, ident, _) => { let mutab = mutab.prefix_str(); ( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6e134bad7bd..5824c2aa405 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3450,10 +3450,8 @@ impl<'a> Parser<'a> { // Detect and recover from `($pat if $cond) => $arm`. // FIXME(guard_patterns): convert this to a normal guard instead let span = pat.span; - let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() }; - let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else { - unreachable!() - }; + let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() }; + let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() }; self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); let right = self.prev_token.span; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index babc55ccc0f..e9654e61eb8 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -145,7 +145,7 @@ impl<'a> Parser<'a> { { let mut item = item.expect("an actual item"); attrs.prepend_to_nt_inner(&mut item.attrs); - return Ok(Some(item.into_inner())); + return Ok(Some(*item)); } self.collect_tokens(None, attrs, force_collect, |this, mut attrs| { @@ -637,7 +637,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(errors::MissingForInTraitImpl { span: missing_for_span }); } - let ty_first = ty_first.into_inner(); + let ty_first = *ty_first; let path = match ty_first.kind { // This notably includes paths passed through `ty` macro fragments (#46438). TyKind::Path(None, path) => path, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index d6ff80b2eb4..7a226136e23 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1086,7 +1086,7 @@ impl<'a> Parser<'a> { if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) { self.psess.gated_spans.gate(sym::mut_ref, pat.span); } - Ok(pat.into_inner().kind) + Ok(pat.kind) } /// Turn all by-value immutable bindings in a pattern into mutable bindings. diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 17481731b11..460cf23c393 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -404,7 +404,7 @@ impl<'a> Parser<'a> { })?; if ts.len() == 1 && matches!(trailing, Trailing::No) { - let ty = ts.into_iter().next().unwrap().into_inner(); + let ty = ts.into_iter().next().unwrap(); let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus(); match ty.kind { // `(TY_BOUND_NOPAREN) + BOUND + ...`. @@ -420,7 +420,7 @@ impl<'a> Parser<'a> { self.parse_remaining_bounds(bounds, true) } // `(TYPE)` - _ => Ok(TyKind::Paren(P(ty))), + _ => Ok(TyKind::Paren(ty)), } } else { Ok(TyKind::Tup(ts)) @@ -1295,7 +1295,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, ()> { let fn_path_segment = fn_path.segments.last_mut().unwrap(); let generic_args = if let Some(p_args) = &fn_path_segment.args { - p_args.clone().into_inner() + *p_args.clone() } else { // Normally it wouldn't come here because the upstream should have parsed // generic parameters (otherwise it's impossible to call this function). diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 9ad184450de..b839b6f5672 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -426,7 +426,7 @@ fn drain_matching( // Check if we should extract, but only if `idx >= start`. if idx > start && predicate(&alternatives[i].kind) { let pat = alternatives.remove(i); - tail_or.push(extract(pat.into_inner().kind)); + tail_or.push(extract(pat.kind)); } else { i += 1; } diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs index bc5a6d3e704..44c8123517c 100644 --- a/src/tools/rustfmt/src/modules.rs +++ b/src/tools/rustfmt/src/modules.rs @@ -174,7 +174,7 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> { ) -> Result<(), ModuleResolutionError> { for item in items { if is_cfg_if(&item) { - self.visit_cfg_if(Cow::Owned(item.into_inner()))?; + self.visit_cfg_if(Cow::Owned(*item))?; continue; } diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index 30b83373c17..26bf6c5326f 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -62,7 +62,7 @@ fn parse_cfg_if_inner<'a>( while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof { let item = match parser.parse_item(ForceCollect::No) { - Ok(Some(item_ptr)) => item_ptr.into_inner(), + Ok(Some(item_ptr)) => *item_ptr, Ok(None) => continue, Err(err) => { err.cancel(); -- cgit 1.4.1-3-g733a5 From c7c0194d980cbb812a61e369b8f92faf75b12f8e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 5 May 2025 15:24:14 +0200 Subject: move asm parsing code into `rustc_parse` --- compiler/rustc_builtin_macros/messages.ftl | 27 +- compiler/rustc_builtin_macros/src/asm.rs | 387 +--------------------------- compiler/rustc_builtin_macros/src/errors.rs | 79 +----- compiler/rustc_parse/messages.ftl | 24 ++ compiler/rustc_parse/src/errors.rs | 70 +++++ compiler/rustc_parse/src/parser/asm.rs | 385 +++++++++++++++++++++++++++ compiler/rustc_parse/src/parser/mod.rs | 1 + src/tools/rustfmt/src/lib.rs | 1 - src/tools/rustfmt/src/parse/macros/asm.rs | 2 +- tests/ui/asm/cfg-parse-error.rs | 5 +- tests/ui/asm/cfg-parse-error.stderr | 19 +- tests/ui/asm/parse-error.stderr | 16 +- 12 files changed, 518 insertions(+), 498 deletions(-) create mode 100644 compiler/rustc_parse/src/parser/asm.rs (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 9e0fe255e99..628bdee1129 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -14,17 +14,6 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}` .label = previously here .arg = duplicate argument -builtin_macros_asm_expected_comma = expected token: `,` - .label = expected `,` - -builtin_macros_asm_expected_other = expected operand, {$is_inline_asm -> - [false] options - *[true] clobber_abi, options - }, or additional template string - -builtin_macros_asm_expected_string_literal = expected string literal - .label = not a string literal - builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option @@ -50,17 +39,8 @@ builtin_macros_asm_pure_combine = the `pure` option must be combined with either builtin_macros_asm_pure_no_output = asm with the `pure` option must have at least one output -builtin_macros_asm_requires_template = requires at least a template string argument - -builtin_macros_asm_sym_no_path = expected a path for argument to `sym` - -builtin_macros_asm_underscore_input = _ cannot be used for input operands - builtin_macros_asm_unsupported_clobber_abi = `clobber_abi` cannot be used with `{$macro_name}!` -builtin_macros_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!` - .label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it - builtin_macros_asm_unsupported_option = the `{$symbol}` option cannot be used with `{$macro_name}!` .label = the `{$symbol}` option is not meaningful for global-scoped inline assembly .suggestion = remove this option @@ -167,7 +147,10 @@ builtin_macros_expected_comma_in_list = expected token: `,` builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern -builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register +builtin_macros_expected_other = expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options + }, or additional template string builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently @@ -260,8 +243,6 @@ builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[defa .label = this enum needs a unit variant marked with `#[default]` .suggestion = make this unit variant default by placing `#[default]` on it -builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi` - builtin_macros_non_exhaustive_default = default variant must be exhaustive .label = declared `#[non_exhaustive]` here .help = consider a manual implementation of `Default` diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 593d9ddfdf8..1fb99817222 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,4 +1,3 @@ -use ast::token::IdentIsRaw; use lint::BuiltinLintDiag; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; @@ -7,11 +6,10 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::PResult; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; -use rustc_parse::exp; -use rustc_parse::parser::{ExpKeywordPair, Parser}; +use rustc_parse::parser::asm::*; use rustc_session::lint; use rustc_session::parse::feature_err; -use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw, sym}; +use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; use {rustc_ast as ast, rustc_parse_format as parse}; @@ -19,30 +17,6 @@ use {rustc_ast as ast, rustc_parse_format as parse}; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; use crate::{errors, fluent_generated as fluent}; -/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise -/// not validated at all. -pub struct AsmArg { - pub kind: AsmArgKind, - pub attributes: AsmAttrVec, - pub span: Span, -} - -pub enum AsmArgKind { - Template(P), - Operand(Option, ast::InlineAsmOperand), - Options(Vec), - ClobberAbi(Vec<(Symbol, Span)>), -} - -pub struct AsmOption { - pub symbol: Symbol, - pub span: Span, - // A bitset, with only the bit for this option's symbol set. - pub options: ast::InlineAsmOptions, - // Used when suggesting to remove an option. - pub span_with_comma: Span, -} - /// Validated assembly arguments, ready for macro expansion. struct ValidatedAsmArgs { pub templates: Vec>, @@ -54,266 +28,6 @@ struct ValidatedAsmArgs { pub options_spans: Vec, } -/// A parsed list of attributes that is not attached to any item. -/// Used to check whether `asm!` arguments are configured out. -pub struct AsmAttrVec(pub ast::AttrVec); - -impl AsmAttrVec { - fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> { - let mut attributes = ast::AttrVec::new(); - while p.token == token::Pound { - let attr = p.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)?; - attributes.push(attr); - } - - Ok(Self(attributes)) - } -} -impl ast::HasAttrs for AsmAttrVec { - // Follows `ast::Expr`. - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; - - fn attrs(&self) -> &[rustc_ast::Attribute] { - &self.0 - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) { - f(&mut self.0) - } -} - -impl ast::HasTokens for AsmAttrVec { - fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> { - None - } - - fn tokens_mut(&mut self) -> Option<&mut Option> { - None - } -} - -/// Used for better error messages when operand types are used that are not -/// supported by the current macro (e.g. `in` or `out` for `global_asm!`) -/// -/// returns -/// -/// - `Ok(true)` if the current token matches the keyword, and was expected -/// - `Ok(false)` if the current token does not match the keyword -/// - `Err(_)` if the current token matches the keyword, but was not expected -fn eat_operand_keyword<'a>( - p: &mut Parser<'a>, - exp: ExpKeywordPair, - asm_macro: AsmMacro, -) -> PResult<'a, bool> { - if matches!(asm_macro, AsmMacro::Asm) { - Ok(p.eat_keyword(exp)) - } else { - let span = p.token.span; - if p.eat_keyword_noexpect(exp.kw) { - // in gets printed as `r#in` otherwise - let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() }; - Err(p.dcx().create_err(errors::AsmUnsupportedOperand { - span, - symbol, - macro_name: asm_macro.macro_name(), - })) - } else { - Ok(false) - } - } -} - -fn parse_asm_operand<'a>( - p: &mut Parser<'a>, - asm_macro: AsmMacro, -) -> PResult<'a, Option> { - let dcx = p.dcx(); - - Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: false } - } - } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: true } - } - } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const()?; - ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(exp!(Sym)) { - let expr = p.parse_expr()?; - let ast::ExprKind::Path(qself, path) = &expr.kind else { - let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); - return Err(err); - }; - let sym = - ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; - ast::InlineAsmOperand::Sym { sym } - } else { - return Ok(None); - })) -} - -// Public for rustfmt. -pub fn parse_asm_args<'a>( - p: &mut Parser<'a>, - sp: Span, - asm_macro: AsmMacro, -) -> PResult<'a, Vec> { - let dcx = p.dcx(); - - if p.token == token::Eof { - return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); - } - - let mut args = Vec::new(); - - let attributes = AsmAttrVec::parse(p)?; - let first_template = p.parse_expr()?; - args.push(AsmArg { - span: first_template.span, - kind: AsmArgKind::Template(first_template), - attributes, - }); - - let mut allow_templates = true; - - while p.token != token::Eof { - if !p.eat(exp!(Comma)) { - if allow_templates { - // After a template string, we always expect *only* a comma... - return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span })); - } else { - // ...after that delegate to `expect` to also include the other expected tokens. - return Err(p.expect(exp!(Comma)).err().unwrap()); - } - } - - // Accept trailing commas. - if p.token == token::Eof { - break; - } - - let attributes = AsmAttrVec::parse(p)?; - let span_start = p.token.span; - - // Parse `clobber_abi`. - if p.eat_keyword(exp!(ClobberAbi)) { - allow_templates = false; - - args.push(AsmArg { - kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), - span: span_start.to(p.prev_token.span), - attributes, - }); - - continue; - } - - // Parse `options`. - if p.eat_keyword(exp!(Options)) { - allow_templates = false; - - args.push(AsmArg { - kind: AsmArgKind::Options(parse_options(p, asm_macro)?), - span: span_start.to(p.prev_token.span), - attributes, - }); - - continue; - } - - // Parse operand names. - let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { - let (ident, _) = p.token.ident().unwrap(); - p.bump(); - p.expect(exp!(Eq))?; - allow_templates = false; - Some(ident.name) - } else { - None - }; - - if let Some(op) = parse_asm_operand(p, asm_macro)? { - allow_templates = false; - - args.push(AsmArg { - span: span_start.to(p.prev_token.span), - kind: AsmArgKind::Operand(name, op), - attributes, - }); - } else if allow_templates { - let template = p.parse_expr()?; - // If it can't possibly expand to a string, provide diagnostics here to include other - // things it could have been. - match template.kind { - ast::ExprKind::Lit(token_lit) - if matches!( - token_lit.kind, - token::LitKind::Str | token::LitKind::StrRaw(_) - ) => {} - ast::ExprKind::MacCall(..) => {} - _ => { - let err = dcx.create_err(errors::AsmExpectedOther { - span: template.span, - is_inline_asm: matches!(asm_macro, AsmMacro::Asm), - }); - return Err(err); - } - } - - args.push(AsmArg { - span: template.span, - kind: AsmArgKind::Template(template), - attributes, - }); - } else { - p.unexpected_any()? - } - } - - Ok(args) -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -559,103 +273,6 @@ fn validate_asm_args<'a>( Ok(validated) } -fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { - p.expect(exp!(OpenParen))?; - - let mut asm_options = Vec::new(); - - while !p.eat(exp!(CloseParen)) { - const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ - (exp!(Pure), ast::InlineAsmOptions::PURE), - (exp!(Nomem), ast::InlineAsmOptions::NOMEM), - (exp!(Readonly), ast::InlineAsmOptions::READONLY), - (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS), - (exp!(Noreturn), ast::InlineAsmOptions::NORETURN), - (exp!(Nostack), ast::InlineAsmOptions::NOSTACK), - (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND), - (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX), - (exp!(Raw), ast::InlineAsmOptions::RAW), - ]; - - 'blk: { - for (exp, options) in OPTIONS { - // Gives a more accurate list of expected next tokens. - let kw_matched = if asm_macro.is_supported_option(options) { - p.eat_keyword(exp) - } else { - p.eat_keyword_noexpect(exp.kw) - }; - - if kw_matched { - let span = p.prev_token.span; - let span_with_comma = - if p.token == token::Comma { span.to(p.token.span) } else { span }; - - asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); - break 'blk; - } - } - - return p.unexpected_any(); - } - - // Allow trailing commas. - if p.eat(exp!(CloseParen)) { - break; - } - p.expect(exp!(Comma))?; - } - - Ok(asm_options) -} - -fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { - p.expect(exp!(OpenParen))?; - - if p.eat(exp!(CloseParen)) { - return Err(p.dcx().create_err(errors::NonABI { span: p.token.span })); - } - - let mut new_abis = Vec::new(); - while !p.eat(exp!(CloseParen)) { - match p.parse_str_lit() { - Ok(str_lit) => { - new_abis.push((str_lit.symbol_unescaped, str_lit.span)); - } - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span })); - } - }; - - // Allow trailing commas - if p.eat(exp!(CloseParen)) { - break; - } - p.expect(exp!(Comma))?; - } - - Ok(new_abis) -} - -fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { - p.expect(exp!(OpenParen))?; - let result = match p.token.uninterpolate().kind { - token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), - token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { - ast::InlineAsmRegOrRegClass::Reg(symbol) - } - _ => { - return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister { - span: p.token.span, - })); - } - }; - p.bump(); - p.expect(exp!(CloseParen))?; - Ok(result) -} - fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, asm_macro: AsmMacro, diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 73e8fed321c..75d06a8df14 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -109,13 +109,6 @@ pub(crate) struct ProcMacro { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_non_abi)] -pub(crate) struct NonABI { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_trace_macros)] pub(crate) struct TraceMacros { @@ -788,13 +781,6 @@ pub(crate) struct AsmModifierInvalid { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_requires_template)] -pub(crate) struct AsmRequiresTemplate { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_asm_attribute_not_supported)] pub(crate) struct AsmAttributeNotSupported { @@ -802,45 +788,6 @@ pub(crate) struct AsmAttributeNotSupported { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_expected_comma)] -pub(crate) struct AsmExpectedComma { - #[primary_span] - #[label] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_expected_string_literal)] -pub(crate) struct AsmExpectedStringLiteral { - #[primary_span] - #[label] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_underscore_input)] -pub(crate) struct AsmUnderscoreInput { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_sym_no_path)] -pub(crate) struct AsmSymNoPath { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_expected_other)] -pub(crate) struct AsmExpectedOther { - #[primary_span] - #[label(builtin_macros_asm_expected_other)] - pub(crate) span: Span, - pub(crate) is_inline_asm: bool, -} - #[derive(Diagnostic)] #[diag(builtin_macros_asm_duplicate_arg)] pub(crate) struct AsmDuplicateArg { @@ -932,16 +879,6 @@ pub(crate) struct AsmUnsupportedOption { pub(crate) macro_name: &'static str, } -#[derive(Diagnostic)] -#[diag(builtin_macros_asm_unsupported_operand)] -pub(crate) struct AsmUnsupportedOperand<'a> { - #[primary_span] - #[label] - pub(crate) span: Span, - pub(crate) symbol: &'a str, - pub(crate) macro_name: &'static str, -} - #[derive(Diagnostic)] #[diag(builtin_macros_asm_unsupported_clobber_abi)] pub(crate) struct AsmUnsupportedClobberAbi { @@ -964,13 +901,6 @@ pub(crate) struct TestRunnerNargs { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_expected_register_class_or_explicit_register)] -pub(crate) struct ExpectedRegisterClassOrExplicitRegister { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_expected_comma_in_list)] pub(crate) struct ExpectedCommaInList { @@ -1034,3 +964,12 @@ pub(crate) struct NonGenericPointee { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(builtin_macros_expected_other)] +pub(crate) struct AsmExpectedOther { + #[primary_span] + #[label(builtin_macros_expected_other)] + pub(crate) span: Span, + pub(crate) is_inline_asm: bool, +} diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a6919afef12..1f221b4bf78 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -8,6 +8,30 @@ parse_array_brackets_instead_of_braces = this is a block expression, not an arra parse_array_index_offset_of = array indexing not supported in offset_of +parse_asm_expected_comma = expected token: `,` + .label = expected `,` + +parse_asm_expected_other = expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options + }, or additional template string + +parse_asm_expected_register_class_or_explicit_register = expected register class or explicit register + +parse_asm_expected_string_literal = expected string literal + .label = not a string literal + +parse_asm_non_abi = at least one abi must be provided as an argument to `clobber_abi` + +parse_asm_requires_template = requires at least a template string argument + +parse_asm_sym_no_path = expected a path for argument to `sym` + +parse_asm_underscore_input = _ cannot be used for input operands + +parse_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!` + .label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it + parse_assignment_else_not_allowed = ... else {"{"} ... {"}"} is not allowed parse_associated_static_item_not_allowed = associated `static` items are not allowed diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 31a48b22cfe..2dba568a258 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3525,3 +3525,73 @@ pub(crate) struct MoveSelfModifier { pub insertion_span: Span, pub modifier: String, } + +#[derive(Diagnostic)] +#[diag(parse_asm_unsupported_operand)] +pub(crate) struct AsmUnsupportedOperand<'a> { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) symbol: &'a str, + pub(crate) macro_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_underscore_input)] +pub(crate) struct AsmUnderscoreInput { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_sym_no_path)] +pub(crate) struct AsmSymNoPath { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_requires_template)] +pub(crate) struct AsmRequiresTemplate { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_comma)] +pub(crate) struct AsmExpectedComma { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_other)] +pub(crate) struct AsmExpectedOther { + #[primary_span] + #[label(parse_asm_expected_other)] + pub(crate) span: Span, + pub(crate) is_inline_asm: bool, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_non_abi)] +pub(crate) struct NonABI { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_string_literal)] +pub(crate) struct AsmExpectedStringLiteral { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_asm_expected_register_class_or_explicit_register)] +pub(crate) struct ExpectedRegisterClassOrExplicitRegister { + #[primary_span] + pub(crate) span: Span, +} diff --git a/compiler/rustc_parse/src/parser/asm.rs b/compiler/rustc_parse/src/parser/asm.rs new file mode 100644 index 00000000000..d4d0612a317 --- /dev/null +++ b/compiler/rustc_parse/src/parser/asm.rs @@ -0,0 +1,385 @@ +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, AsmMacro}; +use rustc_span::{Span, Symbol, kw}; + +use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos}; +use crate::{PResult, Parser, errors, exp, token}; + +/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise +/// not validated at all. +pub struct AsmArg { + pub kind: AsmArgKind, + pub attributes: AsmAttrVec, + pub span: Span, +} + +pub enum AsmArgKind { + Template(P), + Operand(Option, ast::InlineAsmOperand), + Options(Vec), + ClobberAbi(Vec<(Symbol, Span)>), +} + +pub struct AsmOption { + pub symbol: Symbol, + pub span: Span, + // A bitset, with only the bit for this option's symbol set. + pub options: ast::InlineAsmOptions, + // Used when suggesting to remove an option. + pub span_with_comma: Span, +} + +/// A parsed list of attributes that is not attached to any item. +/// Used to check whether `asm!` arguments are configured out. +pub struct AsmAttrVec(pub ast::AttrVec); + +impl AsmAttrVec { + fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> { + let attrs = p.parse_outer_attributes()?; + + p.collect_tokens(None, attrs, ForceCollect::No, |_, attrs| { + Ok((Self(attrs), Trailing::No, UsePreAttrPos::No)) + }) + } +} +impl ast::HasAttrs for AsmAttrVec { + // Follows `ast::Expr`. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + + fn attrs(&self) -> &[rustc_ast::Attribute] { + &self.0 + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) { + f(&mut self.0) + } +} + +impl ast::HasTokens for AsmAttrVec { + fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> { + None + } + + fn tokens_mut(&mut self) -> Option<&mut Option> { + None + } +} + +/// Used for better error messages when operand types are used that are not +/// supported by the current macro (e.g. `in` or `out` for `global_asm!`) +/// +/// returns +/// +/// - `Ok(true)` if the current token matches the keyword, and was expected +/// - `Ok(false)` if the current token does not match the keyword +/// - `Err(_)` if the current token matches the keyword, but was not expected +fn eat_operand_keyword<'a>( + p: &mut Parser<'a>, + exp: ExpKeywordPair, + asm_macro: AsmMacro, +) -> PResult<'a, bool> { + if matches!(asm_macro, AsmMacro::Asm) { + Ok(p.eat_keyword(exp)) + } else { + let span = p.token.span; + if p.eat_keyword_noexpect(exp.kw) { + // in gets printed as `r#in` otherwise + let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() }; + Err(p.dcx().create_err(errors::AsmUnsupportedOperand { + span, + symbol, + macro_name: asm_macro.macro_name(), + })) + } else { + Ok(false) + } + } +} + +fn parse_asm_operand<'a>( + p: &mut Parser<'a>, + asm_macro: AsmMacro, +) -> PResult<'a, Option> { + let dcx = p.dcx(); + + Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: false } + } + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: true } + } + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } + } else if p.eat_keyword(exp!(Const)) { + let anon_const = p.parse_expr_anon_const()?; + ast::InlineAsmOperand::Const { anon_const } + } else if p.eat_keyword(exp!(Sym)) { + let expr = p.parse_expr()?; + let ast::ExprKind::Path(qself, path) = &expr.kind else { + let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); + return Err(err); + }; + let sym = + ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; + ast::InlineAsmOperand::Sym { sym } + } else { + return Ok(None); + })) +} + +// Public for rustfmt. +pub fn parse_asm_args<'a>( + p: &mut Parser<'a>, + sp: Span, + asm_macro: AsmMacro, +) -> PResult<'a, Vec> { + let dcx = p.dcx(); + + if p.token == token::Eof { + return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); + } + + let mut args = Vec::new(); + + let attributes = AsmAttrVec::parse(p)?; + let first_template = p.parse_expr()?; + args.push(AsmArg { + span: first_template.span, + kind: AsmArgKind::Template(first_template), + attributes, + }); + + let mut allow_templates = true; + + while p.token != token::Eof { + if !p.eat(exp!(Comma)) { + if allow_templates { + // After a template string, we always expect *only* a comma... + return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span })); + } else { + // ...after that delegate to `expect` to also include the other expected tokens. + return Err(p.expect(exp!(Comma)).err().unwrap()); + } + } + + // Accept trailing commas. + if p.token == token::Eof { + break; + } + + let attributes = AsmAttrVec::parse(p)?; + let span_start = p.token.span; + + // Parse `clobber_abi`. + if p.eat_keyword(exp!(ClobberAbi)) { + allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + span: span_start.to(p.prev_token.span), + attributes, + }); + + continue; + } + + // Parse `options`. + if p.eat_keyword(exp!(Options)) { + allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::Options(parse_options(p, asm_macro)?), + span: span_start.to(p.prev_token.span), + attributes, + }); + + continue; + } + + // Parse operand names. + let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { + let (ident, _) = p.token.ident().unwrap(); + p.bump(); + p.expect(exp!(Eq))?; + allow_templates = false; + Some(ident.name) + } else { + None + }; + + if let Some(op) = parse_asm_operand(p, asm_macro)? { + allow_templates = false; + + args.push(AsmArg { + span: span_start.to(p.prev_token.span), + kind: AsmArgKind::Operand(name, op), + attributes, + }); + } else if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } + } + + args.push(AsmArg { + span: template.span, + kind: AsmArgKind::Template(template), + attributes, + }); + } else { + p.unexpected_any()? + } + } + + Ok(args) +} + +fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { + p.expect(exp!(OpenParen))?; + + let mut asm_options = Vec::new(); + + while !p.eat(exp!(CloseParen)) { + const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ + (exp!(Pure), ast::InlineAsmOptions::PURE), + (exp!(Nomem), ast::InlineAsmOptions::NOMEM), + (exp!(Readonly), ast::InlineAsmOptions::READONLY), + (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS), + (exp!(Noreturn), ast::InlineAsmOptions::NORETURN), + (exp!(Nostack), ast::InlineAsmOptions::NOSTACK), + (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND), + (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX), + (exp!(Raw), ast::InlineAsmOptions::RAW), + ]; + + 'blk: { + for (exp, options) in OPTIONS { + // Gives a more accurate list of expected next tokens. + let kw_matched = if asm_macro.is_supported_option(options) { + p.eat_keyword(exp) + } else { + p.eat_keyword_noexpect(exp.kw) + }; + + if kw_matched { + let span = p.prev_token.span; + let span_with_comma = + if p.token == token::Comma { span.to(p.token.span) } else { span }; + + asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); + break 'blk; + } + } + + return p.unexpected_any(); + } + + // Allow trailing commas. + if p.eat(exp!(CloseParen)) { + break; + } + p.expect(exp!(Comma))?; + } + + Ok(asm_options) +} + +fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { + p.expect(exp!(OpenParen))?; + + if p.eat(exp!(CloseParen)) { + return Err(p.dcx().create_err(errors::NonABI { span: p.token.span })); + } + + let mut new_abis = Vec::new(); + while !p.eat(exp!(CloseParen)) { + match p.parse_str_lit() { + Ok(str_lit) => { + new_abis.push((str_lit.symbol_unescaped, str_lit.span)); + } + Err(opt_lit) => { + let span = opt_lit.map_or(p.token.span, |lit| lit.span); + return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span })); + } + }; + + // Allow trailing commas + if p.eat(exp!(CloseParen)) { + break; + } + p.expect(exp!(Comma))?; + } + + Ok(new_abis) +} + +fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { + p.expect(exp!(OpenParen))?; + let result = match p.token.uninterpolate().kind { + token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), + token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { + ast::InlineAsmRegOrRegClass::Reg(symbol) + } + _ => { + return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister { + span: p.token.span, + })); + } + }; + p.bump(); + p.expect(exp!(CloseParen))?; + Ok(result) +} diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 968376678f3..b2e90251367 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1,3 +1,4 @@ +pub mod asm; pub mod attr; mod attr_wrapper; mod diagnostics; diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index 08cda6913b9..942b42ec5f2 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -8,7 +8,6 @@ // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_builtin_macros; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_expand; diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 1a9614bacec..bfa9c6300c4 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{AsmArg, parse_asm_args}; +use rustc_parse::parser::asm::{AsmArg, parse_asm_args}; use crate::rewrite::RewriteContext; diff --git a/tests/ui/asm/cfg-parse-error.rs b/tests/ui/asm/cfg-parse-error.rs index c66a627ca94..9b79d16a76d 100644 --- a/tests/ui/asm/cfg-parse-error.rs +++ b/tests/ui/asm/cfg-parse-error.rs @@ -14,7 +14,7 @@ fn main() { #[cfg(false)] a = out(reg) x, "", - //~^ ERROR expected one of `clobber_abi`, `const` + //~^ ERROR expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` ); asm!( #[cfg(false)] @@ -23,7 +23,8 @@ fn main() { const { 5 }, - "", //~ ERROR expected one of `clobber_abi`, `const` + "", + //~^ ERROR expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` ); asm!( diff --git a/tests/ui/asm/cfg-parse-error.stderr b/tests/ui/asm/cfg-parse-error.stderr index 19c76adee63..8a70d39a43d 100644 --- a/tests/ui/asm/cfg-parse-error.stderr +++ b/tests/ui/asm/cfg-parse-error.stderr @@ -1,36 +1,39 @@ -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` --> $DIR/cfg-parse-error.rs:16:13 | LL | a = out(reg) x, - | - expected one of 10 possible tokens + | - expected one of 11 possible tokens LL | "", | ^^ unexpected token -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` --> $DIR/cfg-parse-error.rs:26:13 | LL | }, - | - expected one of 10 possible tokens + | - expected one of 11 possible tokens LL | "", | ^^ unexpected token error: expected token: `,` - --> $DIR/cfg-parse-error.rs:40:26 + --> $DIR/cfg-parse-error.rs:41:26 | LL | a = out(reg) x, | ^ expected `,` error: this attribute is not supported on assembly - --> $DIR/cfg-parse-error.rs:46:13 + --> $DIR/cfg-parse-error.rs:47:13 | LL | #[rustfmt::skip] | ^^^^^^^^^^^^^^^^ -error: this attribute is not supported on assembly - --> $DIR/cfg-parse-error.rs:52:13 +error: an inner attribute is not permitted in this context + --> $DIR/cfg-parse-error.rs:53:13 | LL | #![rustfmt::skip] | ^^^^^^^^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + = note: outer attributes, like `#[test]`, annotate the item following them error: aborting due to 5 previous errors diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 0bba1fd8d9b..dff85a601b7 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -176,17 +176,17 @@ LL | asm!("{a}", a = const foo, a = const bar); | = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` --> $DIR/parse-error.rs:80:29 | LL | asm!("", options(), ""); - | ^^ expected one of 10 possible tokens + | ^^ expected one of 11 possible tokens -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` +error: expected one of `#`, `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` --> $DIR/parse-error.rs:82:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); - | ^^^^ expected one of 10 possible tokens + | ^^^^ expected one of 11 possible tokens error: asm template must be a string literal --> $DIR/parse-error.rs:84:14 @@ -340,17 +340,17 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` +error: expected one of `#`, `clobber_abi`, `const`, `options`, or `sym`, found `""` --> $DIR/parse-error.rs:137:28 | LL | global_asm!("", options(), ""); - | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` + | ^^ expected one of `#`, `clobber_abi`, `const`, `options`, or `sym` -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` +error: expected one of `#`, `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` --> $DIR/parse-error.rs:139:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); - | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` + | ^^^^ expected one of `#`, `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal --> $DIR/parse-error.rs:141:13 -- cgit 1.4.1-3-g733a5 From adcd0bf5c36ee49acf390f0d75125da4efda35ac Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 28 May 2025 09:34:08 +0800 Subject: Fix ICE in tokenstream with contracts from parser recovery --- compiler/rustc_parse/src/parser/stmt.rs | 13 ++++----- tests/crashes/140683.rs | 5 ---- ...ce-in-tokenstream-for-contracts-issue-140683.rs | 13 +++++++++ ...n-tokenstream-for-contracts-issue-140683.stderr | 34 ++++++++++++++++++++++ tests/ui/macros/no-close-delim-issue-139248.rs | 5 ++-- tests/ui/macros/no-close-delim-issue-139248.stderr | 16 ++++------ 6 files changed, 60 insertions(+), 26 deletions(-) delete mode 100644 tests/crashes/140683.rs create mode 100644 tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs create mode 100644 tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 396ded96bde..ccc3410674b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -515,8 +515,8 @@ impl<'a> Parser<'a> { fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> { let prev = self.prev_token.span; let sp = self.token.span; - let mut e = self.dcx().struct_span_err(sp, msg); - self.label_expected_raw_ref(&mut e); + let mut err = self.dcx().struct_span_err(sp, msg); + self.label_expected_raw_ref(&mut err); let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon @@ -558,20 +558,19 @@ impl<'a> Parser<'a> { stmt.span }; self.suggest_fixes_misparsed_for_loop_head( - &mut e, + &mut err, prev.between(sp), stmt_span, &stmt.kind, ); } Err(e) => { - self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); - e.cancel(); + e.delay_as_bug(); } _ => {} } - e.span_label(sp, "expected `{`"); - e + err.span_label(sp, "expected `{`"); + err } fn suggest_fixes_misparsed_for_loop_head( diff --git a/tests/crashes/140683.rs b/tests/crashes/140683.rs deleted file mode 100644 index 74ea5c2533b..00000000000 --- a/tests/crashes/140683.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: #140683 -impl T { -#[core::contracts::ensures] - fn b() { (loop) } -} diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs new file mode 100644 index 00000000000..68346a00ae1 --- /dev/null +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -0,0 +1,13 @@ +#![feature(contracts)] +#![allow(incomplete_features)] + +struct T; + +impl T { + #[core::contracts::ensures] //~ ERROR expected a `Fn(&_)` closure, found `()` + fn b() {(loop)} + //~^ ERROR expected `{`, found `)` + //~| ERROR expected `{`, found `)` +} + +fn main() {} diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr new file mode 100644 index 00000000000..f1ffda2a9be --- /dev/null +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -0,0 +1,34 @@ +error: expected `{`, found `)` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 + | +LL | fn b() {(loop)} + | ----^ expected `{` + | | + | while parsing this `loop` expression + +error: expected `{`, found `)` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 + | +LL | fn b() {(loop)} + | ----^ expected `{` + | | + | while parsing this `loop` expression + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: expected a `Fn(&_)` closure, found `()` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected an `Fn(&_)` closure, found `()` + | required by a bound introduced by this call + | + = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/macros/no-close-delim-issue-139248.rs b/tests/ui/macros/no-close-delim-issue-139248.rs index 86583b2724e..f15234eaff1 100644 --- a/tests/ui/macros/no-close-delim-issue-139248.rs +++ b/tests/ui/macros/no-close-delim-issue-139248.rs @@ -2,9 +2,8 @@ macro_rules! m { (static a : () = $e:expr) => { - static a : () = $e; - //~^ ERROR macro expansion ends with an incomplete expression: expected expression - } + static a: () = $e; + }; } m! { static a : () = (if b) } diff --git a/tests/ui/macros/no-close-delim-issue-139248.stderr b/tests/ui/macros/no-close-delim-issue-139248.stderr index 6ed41ae9b46..8aa39851b4b 100644 --- a/tests/ui/macros/no-close-delim-issue-139248.stderr +++ b/tests/ui/macros/no-close-delim-issue-139248.stderr @@ -1,33 +1,27 @@ error: expected `{`, found `)` - --> $DIR/no-close-delim-issue-139248.rs:10:27 + --> $DIR/no-close-delim-issue-139248.rs:9:27 | LL | m! { static a : () = (if b) } | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/no-close-delim-issue-139248.rs:10:26 + --> $DIR/no-close-delim-issue-139248.rs:9:26 | LL | m! { static a : () = (if b) } | ^ error: expected `{`, found `)` - --> $DIR/no-close-delim-issue-139248.rs:10:27 + --> $DIR/no-close-delim-issue-139248.rs:9:27 | LL | m! { static a : () = (if b) } | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/no-close-delim-issue-139248.rs:10:26 + --> $DIR/no-close-delim-issue-139248.rs:9:26 | LL | m! { static a : () = (if b) } | ^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: macro expansion ends with an incomplete expression: expected expression - --> $DIR/no-close-delim-issue-139248.rs:5:28 - | -LL | static a : () = $e; - | ^ expected expression - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 4c4a40f6dfa7630a8817748a687cfdd1335a7358 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 May 2025 08:20:30 +1000 Subject: Reorder `ast::ItemKind::{Struct,Enum,Union}` fields. So they match the order of the parts in the source code, e.g.: ``` struct Foo { t: T, u: U } <-><----> <------------> / | \ ident generics variant_data ``` --- compiler/rustc_ast/src/ast.rs | 20 +++++++++----------- compiler/rustc_ast/src/visit.rs | 6 +++--- compiler/rustc_ast_lowering/src/item.rs | 6 +++--- compiler/rustc_ast_passes/src/ast_validation.rs | 6 +++--- compiler/rustc_ast_pretty/src/pprust/state/item.rs | 8 ++++---- compiler/rustc_builtin_macros/src/deriving/clone.rs | 4 ++-- .../src/deriving/cmp/partial_ord.rs | 2 +- .../src/deriving/coerce_pointee.rs | 2 +- .../rustc_builtin_macros/src/deriving/generic/mod.rs | 6 +++--- compiler/rustc_expand/src/base.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 6 +++--- compiler/rustc_resolve/src/build_reduced_graph.rs | 4 ++-- compiler/rustc_resolve/src/def_collector.rs | 4 ++-- compiler/rustc_resolve/src/effective_visibilities.rs | 4 ++-- compiler/rustc_resolve/src/late.rs | 12 ++++++------ .../src/field_scoped_visibility_modifiers.rs | 2 +- .../clippy/clippy_lints/src/partial_pub_fields.rs | 2 +- src/tools/clippy/clippy_utils/src/ast_utils/mod.rs | 8 ++++---- src/tools/rustfmt/src/items.rs | 4 ++-- src/tools/rustfmt/src/visitor.rs | 2 +- 20 files changed, 54 insertions(+), 56 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a16219361c0..2ececee8751 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3417,9 +3417,9 @@ impl Item { ItemKind::Fn(i) => Some(&i.generics), ItemKind::TyAlias(i) => Some(&i.generics), ItemKind::TraitAlias(_, generics, _) - | ItemKind::Enum(_, _, generics) - | ItemKind::Struct(_, _, generics) - | ItemKind::Union(_, _, generics) => Some(&generics), + | ItemKind::Enum(_, generics, _) + | ItemKind::Struct(_, generics, _) + | ItemKind::Union(_, generics, _) => Some(&generics), ItemKind::Trait(i) => Some(&i.generics), ItemKind::Impl(i) => Some(&i.generics), } @@ -3663,15 +3663,15 @@ pub enum ItemKind { /// An enum definition (`enum`). /// /// E.g., `enum Foo { C, D }`. - Enum(Ident, EnumDef, Generics), + Enum(Ident, Generics, EnumDef), /// A struct definition (`struct`). /// /// E.g., `struct Foo { x: A }`. - Struct(Ident, VariantData, Generics), + Struct(Ident, Generics, VariantData), /// A union definition (`union`). /// /// E.g., `union Foo { x: A, y: B }`. - Union(Ident, VariantData, Generics), + Union(Ident, Generics, VariantData), /// A trait declaration (`trait`). /// /// E.g., `trait Foo { .. }`, `trait Foo { .. }` or `auto trait Foo {}`. @@ -3688,10 +3688,8 @@ pub enum ItemKind { /// /// E.g., `foo!(..)`. MacCall(P), - /// A macro definition. MacroDef(Ident, MacroDef), - /// A single delegation item (`reuse`). /// /// E.g. `reuse ::name { target_expr_template }`. @@ -3767,9 +3765,9 @@ impl ItemKind { Self::Fn(box Fn { generics, .. }) | Self::TyAlias(box TyAlias { generics, .. }) | Self::Const(box ConstItem { generics, .. }) - | Self::Enum(_, _, generics) - | Self::Struct(_, _, generics) - | Self::Union(_, _, generics) + | Self::Enum(_, generics, _) + | Self::Struct(_, generics, _) + | Self::Union(_, generics, _) | Self::Trait(box Trait { generics, .. }) | Self::TraitAlias(_, generics, _) | Self::Impl(box Impl { generics, .. }) => Some(generics), diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index bf5c402e52e..1cc11b58dd9 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -508,7 +508,7 @@ macro_rules! common_visitor_and_walkers { )? $(>::Result::output())? } - ItemKind::Enum(ident, enum_definition, generics) => { + ItemKind::Enum(ident, generics, enum_definition) => { try_visit!(vis.visit_ident(ident)); try_visit!(vis.visit_generics(generics)); $(${ignore($mut)} @@ -516,8 +516,8 @@ macro_rules! common_visitor_and_walkers { )? $(${ignore($lt)}vis.visit_enum_def(enum_definition))? } - ItemKind::Struct(ident, variant_data, generics) - | ItemKind::Union(ident, variant_data, generics) => { + ItemKind::Struct(ident, generics, variant_data) + | ItemKind::Union(ident, generics, variant_data) => { try_visit!(vis.visit_ident(ident)); try_visit!(vis.visit_generics(generics)); vis.visit_variant_data(variant_data) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index e98d6c50ee7..7f7d45790ee 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -306,7 +306,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::ItemKind::TyAlias(ident, ty, generics) } - ItemKind::Enum(ident, enum_definition, generics) => { + ItemKind::Enum(ident, generics, enum_definition) => { let ident = self.lower_ident(*ident); let (generics, variants) = self.lower_generics( generics, @@ -320,7 +320,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::ItemKind::Enum(ident, hir::EnumDef { variants }, generics) } - ItemKind::Struct(ident, struct_def, generics) => { + ItemKind::Struct(ident, generics, struct_def) => { let ident = self.lower_ident(*ident); let (generics, struct_def) = self.lower_generics( generics, @@ -330,7 +330,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::ItemKind::Struct(ident, struct_def, generics) } - ItemKind::Union(ident, vdata, generics) => { + ItemKind::Union(ident, generics, vdata) => { let ident = self.lower_ident(*ident); let (generics, vdata) = self.lower_generics( generics, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index cbf4f2f5eb2..d6fe04d2994 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1010,7 +1010,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); self.extern_mod_span = old_item; } - ItemKind::Enum(_, def, _) => { + ItemKind::Enum(_, _, def) => { for variant in &def.variants { self.visibility_not_permitted( &variant.vis, @@ -1061,7 +1061,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } visit::walk_item(self, item) } - ItemKind::Struct(ident, vdata, generics) => match vdata { + ItemKind::Struct(ident, generics, vdata) => match vdata { VariantData::Struct { fields, .. } => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); @@ -1070,7 +1070,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } _ => visit::walk_item(self, item), }, - ItemKind::Union(ident, vdata, generics) => { + ItemKind::Union(ident, generics, vdata) => { if vdata.fields().is_empty() { self.dcx().emit_err(errors::FieldlessUnion { span: item.span }); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 70cf2f2a459..3638eb31c61 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -298,14 +298,14 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Enum(ident, enum_definition, params) => { - self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis); + ast::ItemKind::Enum(ident, generics, enum_definition) => { + self.print_enum_def(enum_definition, generics, *ident, item.span, &item.vis); } - ast::ItemKind::Struct(ident, struct_def, generics) => { + ast::ItemKind::Struct(ident, generics, struct_def) => { let (cb, ib) = self.head(visibility_qualified(&item.vis, "struct")); self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } - ast::ItemKind::Union(ident, struct_def, generics) => { + ast::ItemKind::Union(ident, generics, struct_def) => { let (cb, ib) = self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 44cf215c662..69f8c273797 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -34,8 +34,8 @@ pub(crate) fn expand_deriving_clone( let is_simple; match item { Annotatable::Item(annitem) => match &annitem.kind { - ItemKind::Struct(_, _, Generics { params, .. }) - | ItemKind::Enum(_, _, Generics { params, .. }) => { + ItemKind::Struct(_, Generics { params, .. }, _) + | ItemKind::Enum(_, Generics { params, .. }, _) => { let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let has_derive_copy = cx.resolver.has_derive_copy(container_id); if has_derive_copy diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index aa01da3151e..0a076dd670b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -21,7 +21,7 @@ pub(crate) fn expand_deriving_partial_ord( // Order in which to perform matching let discr_then_data = if let Annotatable::Item(item) = item - && let ItemKind::Enum(_, def, _) = &item.kind + && let ItemKind::Enum(_, _, def) = &item.kind { let dataful: Vec = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect(); match dataful.iter().filter(|&&b| b).count() { diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 446d8afeedd..0794192621a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -30,7 +30,7 @@ pub(crate) fn expand_deriving_coerce_pointee( item.visit_with(&mut DetectNonGenericPointeeAttr { cx }); let (name_ident, generics) = if let Annotatable::Item(aitem) = item - && let ItemKind::Struct(ident, struct_data, g) = &aitem.kind + && let ItemKind::Struct(ident, g, struct_data) = &aitem.kind { if !matches!( struct_data, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 9aa53f9e4f7..f1bef526c10 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -488,7 +488,7 @@ impl<'a> TraitDef<'a> { ); let newitem = match &item.kind { - ast::ItemKind::Struct(ident, struct_def, generics) => self.expand_struct_def( + ast::ItemKind::Struct(ident, generics, struct_def) => self.expand_struct_def( cx, struct_def, *ident, @@ -496,7 +496,7 @@ impl<'a> TraitDef<'a> { from_scratch, is_packed, ), - ast::ItemKind::Enum(ident, enum_def, generics) => { + ast::ItemKind::Enum(ident, generics, enum_def) => { // We ignore `is_packed` here, because `repr(packed)` // enums cause an error later on. // @@ -504,7 +504,7 @@ impl<'a> TraitDef<'a> { // downstream in blatantly illegal code, so it is fine. self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch) } - ast::ItemKind::Union(ident, struct_def, generics) => { + ast::ItemKind::Union(ident, generics, struct_def) => { if self.supports_unions { self.expand_struct_def( cx, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 55751aa4908..2accfba383e 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1424,7 +1424,7 @@ pub fn parse_macro_name_and_helper_attrs( /// See #73345 and #83125 for more details. /// FIXME(#73933): Remove this eventually. fn pretty_printing_compatibility_hack(item: &Item, psess: &ParseSess) { - if let ast::ItemKind::Enum(ident, enum_def, _) = &item.kind + if let ast::ItemKind::Enum(ident, _, enum_def) = &item.kind && ident.name == sym::ProceduralMasqueradeDummyType && let [variant] = &*enum_def.variants && variant.ident.name == sym::Input diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index babc55ccc0f..c7b0eb11e5a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1577,7 +1577,7 @@ impl<'a> Parser<'a> { }; let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() }; - Ok(ItemKind::Enum(ident, enum_definition, generics)) + Ok(ItemKind::Enum(ident, generics, enum_definition)) } fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option> { @@ -1732,7 +1732,7 @@ impl<'a> Parser<'a> { return Err(self.dcx().create_err(err)); }; - Ok(ItemKind::Struct(ident, vdata, generics)) + Ok(ItemKind::Struct(ident, generics, vdata)) } /// Parses `union Foo { ... }`. @@ -1764,7 +1764,7 @@ impl<'a> Parser<'a> { return Err(err); }; - Ok(ItemKind::Union(ident, vdata, generics)) + Ok(ItemKind::Union(ident, generics, vdata)) } /// This function parses the fields of record structs: diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3460c53782f..c30ed781f35 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -823,7 +823,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } // These items live in both the type and value namespaces. - ItemKind::Struct(ident, ref vdata, _) => { + ItemKind::Struct(ident, _, ref vdata) => { self.build_reduced_graph_for_struct_variant( vdata.fields(), ident, @@ -874,7 +874,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } } - ItemKind::Union(ident, ref vdata, _) => { + ItemKind::Union(ident, _, ref vdata) => { self.build_reduced_graph_for_struct_variant( vdata.fields(), ident, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 13dfb59f27f..25485be5622 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -162,8 +162,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { self.with_parent(def_id, |this| { this.with_impl_trait(ImplTraitContext::Existential, |this| { match i.kind { - ItemKind::Struct(_, ref struct_def, _) - | ItemKind::Union(_, ref struct_def, _) => { + ItemKind::Struct(_, _, ref struct_def) + | ItemKind::Union(_, _, ref struct_def) => { // If this is a unit or tuple-like struct, register the constructor. if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) { this.create_def( diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index a5ca4565d7b..5de80de3f8d 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -252,7 +252,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> self.current_private_vis = prev_private_vis; } - ast::ItemKind::Enum(_, EnumDef { ref variants }, _) => { + ast::ItemKind::Enum(_, _, EnumDef { ref variants }) => { self.set_bindings_effective_visibilities(def_id); for variant in variants { let variant_def_id = self.r.local_def_id(variant.id); @@ -262,7 +262,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> } } - ast::ItemKind::Struct(_, ref def, _) | ast::ItemKind::Union(_, ref def, _) => { + ast::ItemKind::Struct(_, _, ref def) | ast::ItemKind::Union(_, _, ref def) => { for field in def.fields() { self.update_field(self.r.local_def_id(field.id), def_id); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fd977a8eb6c..4cfa079e49b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2694,9 +2694,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_define_opaques(define_opaque); } - ItemKind::Enum(_, _, ref generics) - | ItemKind::Struct(_, _, ref generics) - | ItemKind::Union(_, _, ref generics) => { + ItemKind::Enum(_, ref generics, _) + | ItemKind::Struct(_, ref generics, _) + | ItemKind::Union(_, ref generics, _) => { self.resolve_adt(item, generics); } @@ -5243,9 +5243,9 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { ItemKind::TyAlias(box TyAlias { generics, .. }) | ItemKind::Const(box ConstItem { generics, .. }) | ItemKind::Fn(box Fn { generics, .. }) - | ItemKind::Enum(_, _, generics) - | ItemKind::Struct(_, _, generics) - | ItemKind::Union(_, _, generics) + | ItemKind::Enum(_, generics, _) + | ItemKind::Struct(_, generics, _) + | ItemKind::Union(_, generics, _) | ItemKind::Impl(box Impl { generics, .. }) | ItemKind::Trait(box Trait { generics, .. }) | ItemKind::TraitAlias(_, generics, _) => { diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index aae8291905d..dfb0b4f103c 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -51,7 +51,7 @@ declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MO impl EarlyLintPass for FieldScopedVisibilityModifiers { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(_, ref st, _) = item.kind else { + let ItemKind::Struct(_, _, ref st) = item.kind else { return; }; for field in st.fields() { diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs index cda752d003f..65e93af9420 100644 --- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs +++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs @@ -41,7 +41,7 @@ declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); impl EarlyLintPass for PartialPubFields { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(_, ref st, _) = item.kind else { + let ItemKind::Struct(_, _, ref st) = item.kind else { return; }; diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 8996b694ed8..a0503a699e6 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -436,11 +436,11 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, - (Enum(li, le, lg), Enum(ri, re, rg)) => { - eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg) + (Enum(li, lg, le), Enum(ri, rg, re)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant) }, - (Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => { - eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg) + (Struct(li, lg, lv), Struct(ri, rg, rv)) | (Union(li, lg, lv), Union(ri, rg, rv)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv) }, ( Trait(box ast::Trait { diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index e79b7803c60..1a3897b51cb 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1110,10 +1110,10 @@ impl<'a> StructParts<'a> { pub(crate) fn from_item(item: &'a ast::Item) -> Self { let (prefix, def, ident, generics) = match item.kind { - ast::ItemKind::Struct(ident, ref def, ref generics) => { + ast::ItemKind::Struct(ident, ref generics, ref def) => { ("struct ", def, ident, generics) } - ast::ItemKind::Union(ident, ref def, ref generics) => ("union ", def, ident, generics), + ast::ItemKind::Union(ident, ref generics, ref def) => ("union ", def, ident, generics), _ => unreachable!(), }; StructParts { diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 16d1f5105d5..f6a9a3f2cd1 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -521,7 +521,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => { self.visit_struct(&StructParts::from_item(item)); } - ast::ItemKind::Enum(ident, ref def, ref generics) => { + ast::ItemKind::Enum(ident, ref generics, ref def) => { self.format_missing_with_indent(source!(self, item.span).lo()); self.visit_enum(ident, &item.vis, def, generics, item.span); self.last_pos = source!(self, item.span).hi(); -- cgit 1.4.1-3-g733a5 From 04391045d0ecfad794481b27a49032c48d2ff2ca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 3 Jun 2025 15:21:33 +1000 Subject: Remove `Path::is_ident`. It checks that a path has a single segment that matches the given symbol, and that there are zero generic arguments. It has a single use. We also have `impl PartialEq for Path` which does exactly the same thing *except* it doesn't check for zero generic arguments, which seems like an oversight. It has numerous uses. This commit removes `Path::is_ident`, adds a test for zero generic arguments to `PartialEq for Path`, and changes the single use of `is_ident` to instead use `==`. --- compiler/rustc_ast/src/ast.rs | 22 +++++++++------------- compiler/rustc_parse/src/parser/expr.rs | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7b103126e45..023bf9bb677 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -99,8 +99,15 @@ pub struct Path { impl PartialEq for Path { #[inline] - fn eq(&self, symbol: &Symbol) -> bool { - matches!(&self.segments[..], [segment] if segment.ident.name == *symbol) + fn eq(&self, name: &Symbol) -> bool { + if let [segment] = self.segments.as_ref() + && segment.args.is_none() + && segment.ident.name == *name + { + true + } else { + false + } } } @@ -120,17 +127,6 @@ impl Path { Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None } } - pub fn is_ident(&self, name: Symbol) -> bool { - if let [segment] = self.segments.as_ref() - && segment.args.is_none() - && segment.ident.name == name - { - true - } else { - false - } - } - pub fn is_global(&self) -> bool { self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1a44f4af8a6..adfea3641e6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -834,7 +834,7 @@ impl<'a> Parser<'a> { // guides recovery in case we write `&raw expr`. if borrow_kind == ast::BorrowKind::Ref && mutbl == ast::Mutability::Not - && matches!(&expr.kind, ExprKind::Path(None, p) if p.is_ident(kw::Raw)) + && matches!(&expr.kind, ExprKind::Path(None, p) if *p == kw::Raw) { self.expected_token_types.insert(TokenType::KwMut); self.expected_token_types.insert(TokenType::KwConst); -- cgit 1.4.1-3-g733a5 From 5fbdfc3e1018072e0c272d19308d72ac5165f01a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 27 Feb 2025 09:51:23 +0000 Subject: Add `iter` macro This adds an `iter!` macro that can be used to create movable generators. This also adds a yield_expr feature so the `yield` keyword can be used within iter! macro bodies. This was needed because several unstable features each need `yield` expressions, so this allows us to stabilize them separately from any individual feature. Co-authored-by: Oli Scherer Co-authored-by: Jieyou Xu Co-authored-by: Travis Cross --- compiler/rustc_ast_lowering/src/expr.rs | 15 ++--- compiler/rustc_ast_passes/src/feature_gate.rs | 7 ++- .../rustc_borrowck/src/type_check/input_output.rs | 2 +- compiler/rustc_builtin_macros/src/iter.rs | 53 ++++++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 2 + .../rustc_const_eval/src/check_consts/check.rs | 7 +-- compiler/rustc_const_eval/src/check_consts/ops.rs | 21 ++++--- compiler/rustc_hir/src/hir.rs | 11 +++- compiler/rustc_hir_typeck/src/closure.rs | 20 ++++-- compiler/rustc_parse/src/parser/stmt.rs | 2 +- compiler/rustc_trait_selection/messages.ftl | 4 +- .../error_reporting/traits/fulfillment_errors.rs | 15 +++-- compiler/rustc_trait_selection/src/errors.rs | 5 +- .../src/traits/select/candidate_assembly.rs | 22 ++++++- library/core/src/iter/mod.rs | 2 + library/core/src/iter/sources.rs | 3 + library/core/src/iter/sources/generator.rs | 29 +++++++++ src/tools/miri/tests/pass/iter_macro.rs | 22 +++++++ tests/ui/coroutine/const_gen_fn.rs | 2 + tests/ui/coroutine/const_gen_fn.stderr | 16 ++++- tests/ui/coroutine/gen_block.none.stderr | 4 +- .../feature-gate-coroutines.e2024.stderr | 8 +-- .../feature-gate-coroutines.none.stderr | 8 +-- .../feature-gates/feature-gate-yield-expr.stderr | 2 +- tests/ui/iterators/generator.rs | 24 ++++++++ tests/ui/iterators/generator_args.rs | 24 ++++++++ tests/ui/iterators/generator_capture.rs | 27 ++++++++ tests/ui/iterators/generator_capture_.rs | 26 ++++++++ tests/ui/iterators/generator_capture_.stderr | 25 ++++++++ tests/ui/iterators/generator_capture_fail.rs | 25 ++++++++ tests/ui/iterators/generator_capture_fail.stderr | 20 ++++++ tests/ui/iterators/generator_capture_fnonce.rs | 32 ++++++++++ tests/ui/iterators/generator_capture_no_lend.rs | 30 +++++++++ tests/ui/iterators/generator_returned_from_fn.rs | 63 +++++++++++++++++++ .../ui/iterators/generator_returned_from_fn.stderr | 70 +++++++++++++++++++++ ...acro-not-async-closure-simplified.narrow.stderr | 18 ++++++ .../iter-macro-not-async-closure-simplified.rs | 29 +++++++++ ...-macro-not-async-closure-simplified.wide.stderr | 17 +++++ .../iter-macro-not-async-closure.narrow.stderr | 72 ++++++++++++++++++++++ tests/ui/iterators/iter-macro-not-async-closure.rs | 32 ++++++++++ .../iterators/iter-macro-not-async-closure.stderr | 67 ++++++++++++++++++++ tests/ui/suggestions/unnamable-types.rs | 2 +- .../issue-53678-coroutine-and-const-fn.rs | 2 +- 43 files changed, 826 insertions(+), 61 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/iter.rs create mode 100644 library/core/src/iter/sources/generator.rs create mode 100644 src/tools/miri/tests/pass/iter_macro.rs create mode 100644 tests/ui/iterators/generator.rs create mode 100644 tests/ui/iterators/generator_args.rs create mode 100644 tests/ui/iterators/generator_capture.rs create mode 100644 tests/ui/iterators/generator_capture_.rs create mode 100644 tests/ui/iterators/generator_capture_.stderr create mode 100644 tests/ui/iterators/generator_capture_fail.rs create mode 100644 tests/ui/iterators/generator_capture_fail.stderr create mode 100644 tests/ui/iterators/generator_capture_fnonce.rs create mode 100644 tests/ui/iterators/generator_capture_no_lend.rs create mode 100644 tests/ui/iterators/generator_returned_from_fn.rs create mode 100644 tests/ui/iterators/generator_returned_from_fn.stderr create mode 100644 tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr create mode 100644 tests/ui/iterators/iter-macro-not-async-closure-simplified.rs create mode 100644 tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.rs create mode 100644 tests/ui/iterators/iter-macro-not-async-closure.stderr (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9f3aed9216c..537d4a2a6af 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,4 +1,3 @@ -use std::assert_matches::assert_matches; use std::ops::ControlFlow; use std::sync::Arc; @@ -1199,11 +1198,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); - assert_matches!( - coroutine_kind, - CoroutineKind::Async { .. }, - "only async closures are supported currently" - ); + let coroutine_desugaring = match coroutine_kind { + CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async, + CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen, + CoroutineKind::AsyncGen { span, .. } => { + span_bug!(span, "only async closures and `iter!` closures are supported currently") + } + }; let body = self.with_new_scopes(fn_decl_span, |this| { let inner_decl = @@ -1247,7 +1248,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Lower this as a `CoroutineClosure`. That will ensure that HIR typeck // knows that a `FnDecl` output type like `-> &str` actually means // "coroutine that returns &str", rather than directly returning a `&str`. - kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async), + kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring), constness: hir::Constness::NotConst, }); hir::ExprKind::Closure(c) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 915613a3913..3682d25d341 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -477,11 +477,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { for span in spans { if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) + && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) { #[allow(rustc::untranslatable_diagnostic)] - // Don't know which of the two features to include in the - // error message, so I am arbitrarily picking one. - feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental") + // Emit yield_expr as the error, since that will be sufficient. You can think of it + // as coroutines and gen_blocks imply yield_expr. + feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental") .emit(); } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index c6b29fe36fd..0c46e0c0c22 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { assert_matches!( self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)), Some(hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, + hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure )), "this needs to be modified if we're lowering non-async closures" diff --git a/compiler/rustc_builtin_macros/src/iter.rs b/compiler/rustc_builtin_macros/src/iter.rs new file mode 100644 index 00000000000..7ad83903a1b --- /dev/null +++ b/compiler/rustc_builtin_macros/src/iter.rs @@ -0,0 +1,53 @@ +use rustc_ast::ptr::P; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token}; +use rustc_errors::PResult; +use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_span::Span; + +pub(crate) fn expand<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + let closure = match parse_closure(cx, sp, tts) { + Ok(parsed) => parsed, + Err(err) => { + return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + } + }; + + ExpandResult::Ready(base::MacEager::expr(closure)) +} + +fn parse_closure<'a>( + cx: &mut ExtCtxt<'a>, + span: Span, + stream: TokenStream, +) -> PResult<'a, P> { + let mut closure_parser = cx.new_parser_from_tts(stream); + + let coroutine_kind = Some(CoroutineKind::Gen { + span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + }); + + let mut closure = closure_parser.parse_expr()?; + match &mut closure.kind { + ast::ExprKind::Closure(c) => { + if let Some(kind) = c.coroutine_kind { + cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`"); + } + c.coroutine_kind = coroutine_kind; + if closure_parser.token != token::Eof { + closure_parser.unexpected()?; + } + Ok(closure) + } + _ => { + cx.dcx().span_err(closure.span, "`iter!` body must be a closure"); + Err(closure_parser.unexpected().unwrap_err()) + } + } +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 667d90429f2..aa52c3bd281 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -47,6 +47,7 @@ mod errors; mod format; mod format_foreign; mod global_allocator; +mod iter; mod log_syntax; mod pattern_type; mod source_util; @@ -95,6 +96,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { include: source_util::expand_include, include_bytes: source_util::expand_include_bytes, include_str: source_util::expand_include_str, + iter: iter::expand, line: source_util::expand_line, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index b67a3ce03a9..6167f8cd4b5 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -589,12 +589,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() - && let Some( - coroutine_kind @ hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, - _, - ), - ) = self.tcx.coroutine_kind(def_id) + && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { self.check_op(ops::Coroutine(coroutine_kind)); } diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 177ba56b165..d701646719a 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -486,24 +486,25 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { pub(crate) struct Coroutine(pub hir::CoroutineKind); impl<'tcx> NonConstOp<'tcx> for Coroutine { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { - if let hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, - hir::CoroutineSource::Block, - ) = self.0 - { - Status::Unstable { + match self.0 { + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Block, + ) + // FIXME(coroutines): eventually we want to gate const coroutine coroutines behind a + // different feature. + | hir::CoroutineKind::Coroutine(_) => Status::Unstable { gate: sym::const_async_blocks, gate_already_checked: false, safe_to_expose_on_stable: false, is_function_call: false, - } - } else { - Status::Forbidden + }, + _ => Status::Forbidden, } } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind()); + let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind()); if let Status::Unstable { gate, .. } = self.status_in_item(ccx) { ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate) } else { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4f05e1c816c..433d5f98829 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2061,12 +2061,19 @@ impl CoroutineKind { CoroutineKind::Coroutine(mov) => mov, } } -} -impl CoroutineKind { pub fn is_fn_like(self) -> bool { matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn)) } + + pub fn to_plural_string(&self) -> String { + match self { + CoroutineKind::Desugared(d, CoroutineSource::Fn) => format!("{d:#}fn bodies"), + CoroutineKind::Desugared(d, CoroutineSource::Block) => format!("{d:#}blocks"), + CoroutineKind::Desugared(d, CoroutineSource::Closure) => format!("{d:#}closure bodies"), + CoroutineKind::Coroutine(_) => "coroutines".to_string(), + } + } } impl fmt::Display for CoroutineKind { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b1cb3ef4d79..cd3746be1d1 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -204,14 +204,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } hir::ClosureKind::CoroutineClosure(kind) => { - // async closures always return the type ascribed after the `->` (if present), - // and yield `()`. let (bound_return_ty, bound_yield_ty) = match kind { + hir::CoroutineDesugaring::Gen => { + // `iter!` closures always return unit and yield the `Iterator::Item` type + // that we have to infer. + (tcx.types.unit, self.infcx.next_ty_var(expr_span)) + } hir::CoroutineDesugaring::Async => { + // async closures always return the type ascribed after the `->` (if present), + // and yield `()`. (bound_sig.skip_binder().output(), tcx.types.unit) } - hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => { - todo!("`gen` and `async gen` closures not supported yet") + hir::CoroutineDesugaring::AsyncGen => { + todo!("`async gen` closures not supported yet") } }; // Compute all of the variables that will be used to populate the coroutine. @@ -465,7 +470,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { - hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id), + hir::ClosureKind::Closure + // FIXME(iter_macro): Someday we'll probably want iterator closures instead of + // just using Fn* for iterators. + | hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => { + self.tcx.fn_trait_kind_from_def_id(trait_def_id) + } hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self .tcx .async_fn_trait_kind_from_def_id(trait_def_id) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ccc3410674b..c37cb0881c3 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -713,7 +713,7 @@ impl<'a> Parser<'a> { /// Parses the rest of a block expression or function body. /// Precondition: already parsed the '{'. - pub(crate) fn parse_block_tail( + pub fn parse_block_tail( &mut self, lo: Span, s: BlockCheckMode, diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 9b949a0a795..8232da4df43 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -72,8 +72,6 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur trait_selection_ascribe_user_type_prove_predicate = ...so that the where clause holds -trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment - trait_selection_await_both_futures = consider `await`ing on both `Future`s trait_selection_await_future = consider `await`ing on the `Future` trait_selection_await_note = calling an async function returns a future @@ -123,6 +121,8 @@ trait_selection_closure_kind_requirement = the requirement to implement `{$trait trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait trait_selection_consider_specifying_length = consider specifying the actual array length +trait_selection_coro_closure_not_fn = {$coro_kind}closure does not implement `{$kind}` because it captures state from its environment + trait_selection_data_flows = ...but data{$label_var1_exists -> [true] {" "}from `{$label_var1}` *[false] {""} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 9b5e421e0e4..fc5be111144 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -42,9 +42,7 @@ use super::{ use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; use crate::error_reporting::traits::report_dyn_incompatibility; -use crate::errors::{ - AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, -}; +use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{ @@ -886,9 +884,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // is unimplemented is because async closures don't implement `Fn`/`FnMut` // if they have captures. if has_self_borrows && expected_kind != ty::ClosureKind::FnOnce { - let mut err = self.dcx().create_err(AsyncClosureNotFn { + let coro_kind = match self + .tcx + .coroutine_kind(self.tcx.coroutine_for_closure(closure_def_id)) + .unwrap() + { + rustc_hir::CoroutineKind::Desugared(desugaring, _) => desugaring.to_string(), + coro => coro.to_string(), + }; + let mut err = self.dcx().create_err(CoroClosureNotFn { span: self.tcx.def_span(closure_def_id), kind: expected_kind.as_str(), + coro_kind, }); self.note_obligation_cause(&mut err, &obligation); return Some(err.emit()); diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 779c861637a..0bea308ed5c 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -201,11 +201,12 @@ pub struct ClosureFnMutLabel { } #[derive(Diagnostic)] -#[diag(trait_selection_async_closure_not_fn)] -pub(crate) struct AsyncClosureNotFn { +#[diag(trait_selection_coro_closure_not_fn)] +pub(crate) struct CoroClosureNotFn { #[primary_span] pub span: Span, pub kind: &'static str, + pub coro_kind: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 7d869b27445..97ecf9702e6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -11,7 +11,7 @@ use std::ops::ControlFlow; use hir::LangItem; use hir::def_id::DefId; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_hir as hir; +use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode, elaborate}; @@ -438,6 +438,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + #[instrument(level = "debug", skip(self, candidates))] fn assemble_async_closure_candidates( &mut self, obligation: &PolyTraitObligation<'tcx>, @@ -446,15 +447,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let goal_kind = self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); + debug!("self_ty = {:?}", obligation.self_ty().skip_binder().kind()); match *obligation.self_ty().skip_binder().kind() { - ty::CoroutineClosure(_, args) => { + ty::CoroutineClosure(def_id, args) => { if let Some(closure_kind) = args.as_coroutine_closure().kind_ty().to_opt_closure_kind() && !closure_kind.extends(goal_kind) { return; } - candidates.vec.push(AsyncClosureCandidate); + + // Make sure this is actually an async closure. + let Some(coroutine_kind) = + self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(def_id)) + else { + bug!("coroutine with no kind"); + }; + + debug!(?coroutine_kind); + match coroutine_kind { + CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { + candidates.vec.push(AsyncClosureCandidate); + } + _ => (), + } } // Closures and fn pointers implement `AsyncFn*` if their return types // implement `Future`, which is checked later. diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d62a445d704..b85841295da 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -420,6 +420,8 @@ pub use self::adapters::{Intersperse, IntersperseWith}; issue = "42168" )] pub use self::range::Step; +#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] +pub use self::sources::iter; #[stable(feature = "iter_empty", since = "1.2.0")] pub use self::sources::{Empty, empty}; #[unstable( diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index 1eb4367b183..fd9330201ff 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -1,6 +1,7 @@ mod empty; mod from_coroutine; mod from_fn; +mod generator; mod once; mod once_with; mod repeat; @@ -18,6 +19,8 @@ pub use self::empty::{Empty, empty}; pub use self::from_coroutine::{FromCoroutine, from_coroutine}; #[stable(feature = "iter_from_fn", since = "1.34.0")] pub use self::from_fn::{FromFn, from_fn}; +#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] +pub use self::generator::iter; #[stable(feature = "iter_once", since = "1.2.0")] pub use self::once::{Once, once}; #[stable(feature = "iter_once_with", since = "1.43.0")] diff --git a/library/core/src/iter/sources/generator.rs b/library/core/src/iter/sources/generator.rs new file mode 100644 index 00000000000..c94232e09eb --- /dev/null +++ b/library/core/src/iter/sources/generator.rs @@ -0,0 +1,29 @@ +/// Creates a new closure that returns an iterator where each iteration steps the given +/// generator to the next `yield` statement. +/// +/// Similar to [`iter::from_fn`], but allows arbitrary control flow. +/// +/// [`iter::from_fn`]: crate::iter::from_fn +/// +/// # Examples +/// +/// ``` +/// #![feature(iter_macro, coroutines)] +/// # #[cfg(not(bootstrap))] +/// # { +/// +/// let it = std::iter::iter!{|| { +/// yield 1; +/// yield 2; +/// yield 3; +/// } }(); +/// let v: Vec<_> = it.collect(); +/// assert_eq!(v, [1, 2, 3]); +/// # } +/// ``` +#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")] +#[allow_internal_unstable(coroutines, iter_from_coroutine)] +#[cfg_attr(not(bootstrap), rustc_builtin_macro)] +pub macro iter($($t:tt)*) { + /* compiler-builtin */ +} diff --git a/src/tools/miri/tests/pass/iter_macro.rs b/src/tools/miri/tests/pass/iter_macro.rs new file mode 100644 index 00000000000..367c13f9969 --- /dev/null +++ b/src/tools/miri/tests/pass/iter_macro.rs @@ -0,0 +1,22 @@ +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = iter! { || { + yield 0; + for x in 5..10 { + yield x * 2; + } + } }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/coroutine/const_gen_fn.rs b/tests/ui/coroutine/const_gen_fn.rs index 2701139ffed..b044c185e0f 100644 --- a/tests/ui/coroutine/const_gen_fn.rs +++ b/tests/ui/coroutine/const_gen_fn.rs @@ -4,8 +4,10 @@ const gen fn a() {} //~^ ERROR functions cannot be both `const` and `gen` +//~^^ ERROR `gen` fn bodies are not allowed in constant functions const async gen fn b() {} //~^ ERROR functions cannot be both `const` and `async gen` +//~^^ ERROR `async gen` fn bodies are not allowed in constant functions fn main() {} diff --git a/tests/ui/coroutine/const_gen_fn.stderr b/tests/ui/coroutine/const_gen_fn.stderr index 4f3c73d1678..400ee216d06 100644 --- a/tests/ui/coroutine/const_gen_fn.stderr +++ b/tests/ui/coroutine/const_gen_fn.stderr @@ -8,7 +8,7 @@ LL | const gen fn a() {} | `const` because of this error: functions cannot be both `const` and `async gen` - --> $DIR/const_gen_fn.rs:8:1 + --> $DIR/const_gen_fn.rs:9:1 | LL | const async gen fn b() {} | ^^^^^-^^^^^^^^^---------- @@ -16,5 +16,17 @@ LL | const async gen fn b() {} | | `async gen` because of this | `const` because of this -error: aborting due to 2 previous errors +error: `gen` fn bodies are not allowed in constant functions + --> $DIR/const_gen_fn.rs:5:18 + | +LL | const gen fn a() {} + | ^^ + +error: `async gen` fn bodies are not allowed in constant functions + --> $DIR/const_gen_fn.rs:9:24 + | +LL | const async gen fn b() {} + | ^^ + +error: aborting due to 4 previous errors diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr index ed744f2957a..b793033b521 100644 --- a/tests/ui/coroutine/gen_block.none.stderr +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -31,7 +31,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -41,7 +41,7 @@ LL | let _ = #[coroutine] || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the `#[coroutine]` attribute is an experimental feature diff --git a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr index 381e7a210be..c29c328ac14 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr @@ -5,7 +5,7 @@ LL | yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -15,7 +15,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -25,7 +25,7 @@ LL | yield; | ^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -35,7 +35,7 @@ LL | yield 0; | ^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental diff --git a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr index 381e7a210be..c29c328ac14 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr @@ -5,7 +5,7 @@ LL | yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -15,7 +15,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -25,7 +25,7 @@ LL | yield; | ^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental @@ -35,7 +35,7 @@ LL | yield 0; | ^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental diff --git a/tests/ui/feature-gates/feature-gate-yield-expr.stderr b/tests/ui/feature-gates/feature-gate-yield-expr.stderr index ad8a15a0f36..bfac9e49803 100644 --- a/tests/ui/feature-gates/feature-gate-yield-expr.stderr +++ b/tests/ui/feature-gates/feature-gate-yield-expr.stderr @@ -5,7 +5,7 @@ LL | yield (); | ^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental diff --git a/tests/ui/iterators/generator.rs b/tests/ui/iterators/generator.rs new file mode 100644 index 00000000000..e633efb11c1 --- /dev/null +++ b/tests/ui/iterators/generator.rs @@ -0,0 +1,24 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = iter! { || { + yield 0; + for x in 5..10 { + yield x * 2; + } + } }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_args.rs b/tests/ui/iterators/generator_args.rs new file mode 100644 index 00000000000..c9da9e5fba6 --- /dev/null +++ b/tests/ui/iterators/generator_args.rs @@ -0,0 +1,24 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = iter! {|foo| { + yield foo; + for x in 5..10 { + yield x * 2; + } + }}; + let mut i = i(3); + assert_eq!(i.next(), Some(3)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture.rs b/tests/ui/iterators/generator_capture.rs new file mode 100644 index 00000000000..c790b7a4b96 --- /dev/null +++ b/tests/ui/iterators/generator_capture.rs @@ -0,0 +1,27 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = { + let s = String::new(); + iter! { move || { + yield s.len(); + for x in 5..10 { + yield x * 2; + } + }} + }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_.rs b/tests/ui/iterators/generator_capture_.rs new file mode 100644 index 00000000000..f630bc64b97 --- /dev/null +++ b/tests/ui/iterators/generator_capture_.rs @@ -0,0 +1,26 @@ +// This test exercises lending behavior for iterator closures which is not yet supported. + +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let f = { + let s = "foo".to_string(); + iter! { move || { + for c in s.chars() { + yield c; + } + }} + }; + let mut i = f(); + assert_eq!(i.next(), Some('f')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), None); + let mut i = f(); //~ ERROR use of moved value: `f` + assert_eq!(i.next(), Some('f')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_.stderr b/tests/ui/iterators/generator_capture_.stderr new file mode 100644 index 00000000000..3d9647ae16f --- /dev/null +++ b/tests/ui/iterators/generator_capture_.stderr @@ -0,0 +1,25 @@ +error[E0382]: use of moved value: `f` + --> $DIR/generator_capture_.rs:21:17 + | +LL | let f = { + | - move occurs because `f` has type `{gen closure@$DIR/generator_capture_.rs:10:17: 10:24}`, which does not implement the `Copy` trait +... +LL | let mut i = f(); + | --- `f` moved due to this call +... +LL | let mut i = f(); + | ^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/generator_capture_.rs:16:17 + | +LL | let mut i = f(); + | ^ +help: consider cloning the value if the performance cost is acceptable + | +LL | let mut i = f.clone()(); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/iterators/generator_capture_fail.rs b/tests/ui/iterators/generator_capture_fail.rs new file mode 100644 index 00000000000..d987b2df011 --- /dev/null +++ b/tests/ui/iterators/generator_capture_fail.rs @@ -0,0 +1,25 @@ +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = { + let s = String::new(); + iter! { || { //~ ERROR `s` does not live long enough + yield s.len(); + for x in 5..10 { + yield x * 2; + } + } } + }; + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_fail.stderr b/tests/ui/iterators/generator_capture_fail.stderr new file mode 100644 index 00000000000..225a385d6a0 --- /dev/null +++ b/tests/ui/iterators/generator_capture_fail.stderr @@ -0,0 +1,20 @@ +error[E0597]: `s` does not live long enough + --> $DIR/generator_capture_fail.rs:8:17 + | +LL | let i = { + | - borrow later stored here +LL | let s = String::new(); +LL | iter! { || { + | _________________^ +LL | | yield s.len(); +LL | | for x in 5..10 { +LL | | yield x * 2; +LL | | } +LL | | } } + | |_________^ borrowed value does not live long enough +LL | }; + | - `s` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/iterators/generator_capture_fnonce.rs b/tests/ui/iterators/generator_capture_fnonce.rs new file mode 100644 index 00000000000..090727eb9b7 --- /dev/null +++ b/tests/ui/iterators/generator_capture_fnonce.rs @@ -0,0 +1,32 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +use std::iter::iter; + +fn main() { + let i = { + let s = String::new(); + iter! { move || { + yield s.len(); + for x in 5..10 { + yield x * 2; + } + }} + }; + test_iterator(i); +} + +/// Exercise the iterator in a separate function to ensure it's not capturing anything it shoudln't. +fn test_iterator>(i: impl FnOnce() -> I) { + let mut i = i(); + assert_eq!(i.next(), Some(0)); + assert_eq!(i.next(), Some(10)); + assert_eq!(i.next(), Some(12)); + assert_eq!(i.next(), Some(14)); + assert_eq!(i.next(), Some(16)); + assert_eq!(i.next(), Some(18)); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); + assert_eq!(i.next(), None); +} diff --git a/tests/ui/iterators/generator_capture_no_lend.rs b/tests/ui/iterators/generator_capture_no_lend.rs new file mode 100644 index 00000000000..822db58d48d --- /dev/null +++ b/tests/ui/iterators/generator_capture_no_lend.rs @@ -0,0 +1,30 @@ +//@ run-pass + +#![feature(iter_macro, yield_expr)] + +// This test creates an iterator that captures a reference and ensure that doesn't force the +// iterator to become lending. + +use std::iter::iter; + +fn main() { + let s = "foo".to_string(); + let f = iter! { || { + for c in s.chars() { + yield c; + } + }}; + + let mut i = f(); + let mut j = f(); + + assert_eq!(i.next(), Some('f')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), Some('o')); + assert_eq!(i.next(), None); + + assert_eq!(j.next(), Some('f')); + assert_eq!(j.next(), Some('o')); + assert_eq!(j.next(), Some('o')); + assert_eq!(j.next(), None); +} diff --git a/tests/ui/iterators/generator_returned_from_fn.rs b/tests/ui/iterators/generator_returned_from_fn.rs new file mode 100644 index 00000000000..bd0317b1ffb --- /dev/null +++ b/tests/ui/iterators/generator_returned_from_fn.rs @@ -0,0 +1,63 @@ +#![feature(iter_macro, impl_trait_in_fn_trait_return, yield_expr)] + +use std::iter::iter; + +fn plain() -> impl Fn() -> impl Iterator { + iter! { || { + yield 0; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn arg() -> impl Fn(u32) -> impl Iterator { + iter! { |arg| { + yield arg; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture<'a>(a: &'a u32) -> impl Fn() -> (impl Iterator + 'a) { + iter! { || { //~ ERROR cannot return reference to function parameter `a` + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture_move(a: &u32) -> impl Fn() -> impl Iterator { + iter! { move || { //~ ERROR does not implement `Fn` because it captures + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { + iter! { move || { + //~^ ERROR captures lifetime + //~| ERROR: captures lifetime + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn capture_move_once_lifetimes<'a>( + a: &'a u32, +) -> impl FnOnce() -> (impl Iterator + 'a) { + iter! { move || { + yield *a; + for x in 5..10 { + yield x * 2; + } + } } +} + +fn main() {} diff --git a/tests/ui/iterators/generator_returned_from_fn.stderr b/tests/ui/iterators/generator_returned_from_fn.stderr new file mode 100644 index 00000000000..b2324af6d5e --- /dev/null +++ b/tests/ui/iterators/generator_returned_from_fn.stderr @@ -0,0 +1,70 @@ +error[E0515]: cannot return reference to function parameter `a` + --> $DIR/generator_returned_from_fn.rs:24:13 + | +LL | iter! { || { + | _____________^ +LL | | yield *a; +LL | | for x in 5..10 { +LL | | yield x * 2; +LL | | } +LL | | } } + | |_____^ returns a reference to data owned by the current function + +error: gen closure does not implement `Fn` because it captures state from its environment + --> $DIR/generator_returned_from_fn.rs:33:13 + | +LL | iter! { move || { + | _____________-^^^^^^ +LL | | yield *a; +LL | | for x in 5..10 { +LL | | yield x * 2; +LL | | } +LL | | } } + | |_____- return type was inferred to be `{gen closure@$DIR/generator_returned_from_fn.rs:33:13: 33:20}` here + +error[E0700]: hidden type for `impl FnOnce() -> impl Iterator` captures lifetime that does not appear in bounds + --> $DIR/generator_returned_from_fn.rs:42:13 + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { + | ---- ------------------------------------------ opaque type defined here + | | + | hidden type `{gen closure@$DIR/generator_returned_from_fn.rs:42:13: 42:20}` captures the anonymous lifetime defined here +LL | iter! { move || { + | _____________^ +LL | | +LL | | +LL | | yield *a; +... | +LL | | } } + | |_____^ + | +help: add a `use<...>` bound to explicitly capture `'_` + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator + use<'_> { + | +++++++++ + +error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds + --> $DIR/generator_returned_from_fn.rs:42:13 + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator { + | ---- ------------------------- opaque type defined here + | | + | hidden type `{gen closure body@$DIR/generator_returned_from_fn.rs:42:21: 49:6}` captures the anonymous lifetime defined here +LL | iter! { move || { + | _____________^ +LL | | +LL | | +LL | | yield *a; +... | +LL | | } } + | |_____^ + | +help: add a `use<...>` bound to explicitly capture `'_` + | +LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator + use<'_> { + | +++++++++ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0515, E0700. +For more information about an error, try `rustc --explain E0515`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr new file mode 100644 index 00000000000..4e0dabade2d --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.narrow.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure-simplified.rs:27:21 + | +LL | call_async_once(f); + | --------------- ^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure-simplified.rs:18:28 + | +LL | ...pl AsyncFnOnce()) {} + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs new file mode 100644 index 00000000000..4fa14fda661 --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.rs @@ -0,0 +1,29 @@ +// This test ensures iterators created with the `iter!` macro are not +// accidentally async closures. +// +// We test this both in a `narrow` and `wide` configuration because +// the way that the diagnostic is emitted varies depending on the +// diagnostic width. If it's too narrow to fit the explanation, that +// explanation is moved to the `help` instead of the span label. +// +//@ edition: 2024 +//@ revisions: narrow wide +//@[narrow] compile-flags: --diagnostic-width=20 +//@[wide] compile-flags: --diagnostic-width=300 + +#![feature(yield_expr, iter_macro)] + +use std::iter::iter; + +fn call_async_once(_: impl AsyncFnOnce()) {} + +fn main() { + let f = iter! { move || { + for i in 0..10 { + yield i; + } + }}; + + call_async_once(f); + //~^ ERROR AsyncFnOnce()` is not satisfied +} diff --git a/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr b/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr new file mode 100644 index 00000000000..a6c239c181b --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure-simplified.wide.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure-simplified.rs:27:21 + | +LL | call_async_once(f); + | --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure-simplified.rs:18:28 + | +LL | fn call_async_once(_: impl AsyncFnOnce()) {} + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr b/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr new file mode 100644 index 00000000000..af3289c3d4e --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.narrow.stderr @@ -0,0 +1,72 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:34 + | +LL | ...n!(call_async_once(f)); + | --------------- ^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:18 + | +LL | ...n!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:13 + | +LL | ... = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:13 + | +LL | ... = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:37:5 + | +LL | ...::noop())); + | ...^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}` +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:21:34 + | +LL | ...pl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure.rs b/tests/ui/iterators/iter-macro-not-async-closure.rs new file mode 100644 index 00000000000..634391883ea --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.rs @@ -0,0 +1,32 @@ +// This test ensures iterators created with the `iter!` macro are not +// accidentally async closures. +// +//@ edition: 2024 +//@ remap-src-base + +#![feature(yield_expr, iter_macro)] + +use std::task::{Waker, Context}; +use std::iter::iter; +use std::pin::pin; +use std::future::Future; + +async fn call_async_once(f: impl AsyncFnOnce()) { + f().await +} + +fn main() { + let f = iter! { move || { + for i in 0..10 { + yield i; + } + }}; + + let x = pin!(call_async_once(f)); + //~^ ERROR AsyncFnOnce()` is not satisfied + //~^^ ERROR AsyncFnOnce()` is not satisfied + //~^^^ ERROR AsyncFnOnce()` is not satisfied + //~^^^^ ERROR AsyncFnOnce()` is not satisfied + x.poll(&mut Context::from_waker(Waker::noop())); + //~^ ERROR AsyncFnOnce()` is not satisfied +} diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.stderr new file mode 100644 index 00000000000..2f0343a2d0d --- /dev/null +++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr @@ -0,0 +1,67 @@ +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:25:34 + | +LL | let x = pin!(call_async_once(f)); + | --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:25:18 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:25:13 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:25:13 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:30:5 + | +LL | x.poll(&mut Context::from_waker(Waker::noop())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/unnamable-types.rs b/tests/ui/suggestions/unnamable-types.rs index 094584ff850..6772217a2f6 100644 --- a/tests/ui/suggestions/unnamable-types.rs +++ b/tests/ui/suggestions/unnamable-types.rs @@ -1,7 +1,7 @@ // Test that we do not suggest to add type annotations for unnamable types. #![crate_type="lib"] -#![feature(coroutines, stmt_expr_attributes)] +#![feature(coroutines, stmt_expr_attributes, const_async_blocks)] const A = 5; //~^ ERROR: missing type for `const` item diff --git a/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs b/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs index f02ccbbb93c..b258d2e156d 100644 --- a/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs +++ b/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs @@ -1,4 +1,4 @@ -#![feature(coroutines, coroutine_trait, rustc_attrs)] +#![feature(coroutines, coroutine_trait, rustc_attrs, const_async_blocks)] #![feature(type_alias_impl_trait)] //@ check-pass -- cgit 1.4.1-3-g733a5 From af2a85bd75c011fb3453a4963400918e096e1896 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 4 Jun 2025 15:16:38 -0700 Subject: Ensure stack in `Parser::parse_ty` This solve a stack overflow found on Fedora s390x when building `tests/ui/associated-consts/issue-93775.rs`. --- compiler/rustc_parse/src/parser/ty.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 17481731b11..6eaec2e29ad 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -7,6 +7,7 @@ use rustc_ast::{ Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, UnsafeBinderTy, }; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult}; use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -104,14 +105,17 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, P> { - self.parse_ty_common( - AllowPlus::Yes, - AllowCVariadic::No, - RecoverQPath::Yes, - RecoverReturnSign::Yes, - None, - RecoverQuestionMark::Yes, - ) + // Make sure deeply nested types don't overflow the stack. + ensure_sufficient_stack(|| { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + ) + }) } pub(super) fn parse_ty_with_generics_recovery( -- cgit 1.4.1-3-g733a5