From 0b09f146e2d2c9961f3b182b63839a2448bdfc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 20 Aug 2026 17:30:41 +0200 Subject: [PATCH 1/8] Implement forced keywords --- compiler/rustc_ast/src/token.rs | 33 ++++++++---- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 10 ++-- compiler/rustc_expand/src/mbe/macro_check.rs | 3 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 6 +-- compiler/rustc_expand/src/mbe/metavar_expr.rs | 27 ++++++++-- compiler/rustc_expand/src/mbe/quoted.rs | 9 ++-- compiler/rustc_expand/src/mbe/transcribe.rs | 16 ++---- .../rustc_expand/src/proc_macro_server.rs | 52 +++++++++++++------ compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_lexer/src/lib.rs | 28 ++++++---- compiler/rustc_parse/src/diagnostics.rs | 8 +++ compiler/rustc_parse/src/lexer/mod.rs | 30 +++++++++++ .../rustc_parse/src/parser/diagnostics.rs | 21 ++++---- compiler/rustc_parse/src/parser/expr.rs | 10 ++-- compiler/rustc_parse/src/parser/item.rs | 3 +- compiler/rustc_parse/src/parser/mod.rs | 11 ++-- compiler/rustc_parse/src/parser/pat.rs | 2 +- compiler/rustc_span/src/symbol.rs | 11 ++++ library/proc_macro/src/bridge/mod.rs | 30 ++++++++++- library/proc_macro/src/lib.rs | 8 +-- src/librustdoc/html/highlight.rs | 1 + .../crates/parser/src/lexed_str.rs | 2 + .../src/legacy_protocol/msg/flat.rs | 12 +++-- .../crates/proc-macro-srv/src/bridge.rs | 2 +- .../crates/proc-macro-srv/src/token_stream.rs | 34 +++++++----- tests/ui/parser/forced-keywords/basic.rs | 23 ++++++++ .../feature-gate-forced-keywords.rs | 6 +++ .../feature-gate-forced-keywords.stderr | 23 ++++++++ .../parser/forced-keywords/invalid-keyword.rs | 6 +++ .../forced-keywords/invalid-keyword.stderr | 14 +++++ .../forced-keywords/pre-2021-edition-fail.rs | 3 ++ .../pre-2021-edition-fail.stderr | 8 +++ .../forced-keywords/pre-2021-edition-pass.rs | 14 +++++ .../pre-2021-edition-pass.stderr | 20 +++++++ 35 files changed, 383 insertions(+), 106 deletions(-) create mode 100644 tests/ui/parser/forced-keywords/basic.rs create mode 100644 tests/ui/parser/forced-keywords/feature-gate-forced-keywords.rs create mode 100644 tests/ui/parser/forced-keywords/feature-gate-forced-keywords.stderr create mode 100644 tests/ui/parser/forced-keywords/invalid-keyword.rs create mode 100644 tests/ui/parser/forced-keywords/invalid-keyword.stderr create mode 100644 tests/ui/parser/forced-keywords/pre-2021-edition-fail.rs create mode 100644 tests/ui/parser/forced-keywords/pre-2021-edition-fail.stderr create mode 100644 tests/ui/parser/forced-keywords/pre-2021-edition-pass.rs create mode 100644 tests/ui/parser/forced-keywords/pre-2021-edition-pass.stderr diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 07da107a55c58..ee693e66eb2e5 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -231,7 +231,7 @@ impl Lit { /// `Parser::eat_token_lit` (excluding unary negation). pub fn from_token(token: &Token) -> Option { match token.uninterpolate().kind { - Ident(name, IdentKind::Normal) if name.is_bool_lit() => { + Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => { Some(Lit::new(Bool, name, None)) } Literal(token_lit) => Some(token_lit), @@ -362,6 +362,7 @@ fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool { pub enum IdentKind { Normal, Raw, + ForcedKeyword, } impl IdentKind { @@ -369,12 +370,15 @@ impl IdentKind { match self { IdentKind::Normal => IdentPrintMode::Normal, IdentKind::Raw => IdentPrintMode::RawIdent, + IdentKind::ForcedKeyword => IdentPrintMode::ForcedKeywordIdent, } } + pub fn to_print_mode_lifetime(self) -> IdentPrintMode { match self { IdentKind::Normal => IdentPrintMode::Normal, IdentKind::Raw => IdentPrintMode::RawLifetime, + IdentKind::ForcedKeyword => unreachable!(), } } } @@ -637,7 +641,10 @@ impl Token { Token::new(TokenKind::Question, DUMMY_SP) } - /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary. + /// Recovers a `Token` from an `Ident`. + /// + /// This creates a raw identifier if necessary. + /// It will never create a forced keyword. pub fn from_ast_ident(ident: sp::Ident) -> Self { let kind = if ident.is_raw_guess() { IdentKind::Raw } else { IdentKind::Normal }; Token::new(Ident(ident.name, kind), ident.span) @@ -763,7 +770,7 @@ impl Token { pub fn can_begin_const_arg(&self) -> bool { match self.kind { OpenBrace | Literal(..) | Minus => true, - Ident(name, IdentKind::Normal) if name.is_bool_lit() => true, + Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => true, OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal, )) => true, @@ -812,7 +819,7 @@ impl Token { pub fn can_begin_literal_maybe_minus(&self) -> bool { match self.uninterpolate().kind { Literal(..) | Minus => true, - Ident(name, IdentKind::Normal) if name.is_bool_lit() => true, + Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => true, OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind { MetaVarKind::Literal => true, MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => { @@ -962,7 +969,7 @@ impl Token { /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { - self.non_raw_ident().is_some_and(sp::Ident::is_reserved) + self.ident().is_some_and(|(id, kind)| ident_of_kind_is_reserved(id, kind)) } pub fn is_non_reserved_ident(&self) -> bool { @@ -970,15 +977,13 @@ impl Token { } pub fn non_reserved_ident(&self) -> Option { - self.ident() - .filter(|&(id, kind)| kind == IdentKind::Raw || !id.is_reserved()) - .map(|(id, _)| id) + self.ident().filter(|&(id, kind)| !ident_of_kind_is_reserved(id, kind)).map(|(id, _)| id) } /// If this token is a non-raw identifier, return the identifier in question. pub fn non_raw_ident(&self) -> Option { match self.ident() { - Some((id, IdentKind::Normal)) => Some(id), + Some((id, IdentKind::Normal | IdentKind::ForcedKeyword)) => Some(id), _ => None, } } @@ -1102,6 +1107,16 @@ impl PartialEq for Token { } } +pub fn ident_of_kind_is_reserved(id: sp::Ident, kind: IdentKind) -> bool { + match kind { + IdentKind::Normal => id.is_reserved(), + IdentKind::Raw => false, + // Purely for better diagnostics we return true here even if the identifier isn't actually + // reserved. That's because clearly the user has requested the identifier to be reserved. + IdentKind::ForcedKeyword => true, + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, StableHash)] pub enum NtPatKind { // Matches or-patterns. Was written using `pat` in edition 2021 or later. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c1134e1dd7300..e1871bc4897cd 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -441,6 +441,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(final_associated_functions, "`final` on trait functions is experimental"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); + gate_all!(forced_keywords, "forced keywords are experimental"); gate_all!(frontmatter, "frontmatters are experimental"); gate_all!(gen_blocks, "gen blocks are experimental"); gate_all!(generic_const_items, "generic const items are experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index d97bf7a2a6db3..73242f521e182 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -370,12 +370,10 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool { // IDENT|`fn`|`Self`|`pub` + `(`: `f(3)`, `fn(x: u8)`, `Self()`, `pub(crate)`, // but `let (a, b) = (1, 2)` needs a space after the `let` - (Tok(tk::Token { kind: tk::Ident(sym, kind), span }, _), Del(_, _, Parenthesis, _)) - if !Ident::new(*sym, *span).is_reserved() - || *sym == kw::Fn - || *sym == kw::SelfUpper - || *sym == kw::Pub - || matches!(kind, tk::IdentKind::Raw) => + (&Tok(tk::Token { kind: tk::Ident(sym, kind), span }, _), Del(_, _, Parenthesis, _)) + if kind == tk::IdentKind::Raw + || matches!(sym, kw::Fn | kw::SelfUpper | kw::Pub) + || !Ident::new(sym, span).is_reserved() => { false } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index e28044d7632a9..f070009fc3d57 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -397,7 +397,8 @@ fn check_nested_occurrences( ( NestedMacroState::Empty, &TokenTree::Token(Token { - kind: TokenKind::Ident(name, IdentKind::Normal), .. + kind: TokenKind::Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword), + .. }), ) => { if name == kw::MacroRules { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index d762763b8dcea..12f308fc546c3 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1750,7 +1750,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { match tok { TokenTree::Token(token) => match token.kind { FatArrow | Comma | Eq | Or => IsInFollow::Yes, - Ident(name, IdentKind::Normal) if name == kw::If || name == kw::In => { + Ident(kw::If | kw::In, IdentKind::Normal | IdentKind::ForcedKeyword) => { IsInFollow::Yes } _ => IsInFollow::No(TOKENS), @@ -1764,7 +1764,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { match tok { TokenTree::Token(token) => match token.kind { FatArrow | Comma | Eq => IsInFollow::Yes, - Ident(name, IdentKind::Normal) if name == kw::If || name == kw::In => { + Ident(kw::If | kw::In, IdentKind::Normal | IdentKind::ForcedKeyword) => { IsInFollow::Yes } _ => IsInFollow::No(TOKENS), @@ -1792,7 +1792,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { TokenTree::Token(token) => match token.kind { OpenBrace | OpenBracket | Comma | FatArrow | Colon | Eq | Gt | Shr | Semi | Or => IsInFollow::Yes, - Ident(name, IdentKind::Normal) if name == kw::As || name == kw::Where => { + Ident(kw::As | kw::Where, IdentKind::Normal | IdentKind::ForcedKeyword) => { IsInFollow::Yes } _ => IsInFollow::No(TOKENS), diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 96b39f0ae6863..812ac1230aa9d 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -9,7 +9,6 @@ use rustc_span::{Ident, Span, Symbol, sym}; use crate::diagnostics; -pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; /// A meta-variable expression, for expansions based on properties of meta-variables. @@ -189,6 +188,10 @@ fn parse_concat<'psess>( } else { match parse_ident_from_token(psess, token) { Err(err) => { + // FIXME: Canceling this error means we emit a worse message when encountering + // raw identifiers (`r#ident`). However, we also don't want to forward + // (the current version of) this error as is since we also want to + // mentioning string literals as a valid token kind. err.cancel(); return Err(psess .dcx() @@ -273,9 +276,7 @@ fn parse_ident_from_token<'psess>( token: &Token, ) -> PResult<'psess, Ident> { if let Some((elem, kind)) = token.ident() { - if let IdentKind::Raw = kind { - return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR)); - } + validate_ident_kind(psess.dcx(), kind, elem.span)?; return Ok(elem); } let token_str = pprust::token_to_string(token); @@ -339,3 +340,21 @@ fn eat_dollar<'psess>( "meta-variables within meta-variable expressions must be referenced using a dollar sign", )) } + +pub(crate) fn validate_ident_kind<'a>( + dcx: rustc_errors::DiagCtxtHandle<'a>, + kind: IdentKind, + span: Span, +) -> PResult<'a, ()> { + Err(dcx.struct_span_err( + span, + format!( + "`${{concat(..)}}` currently does not support {}", + match kind { + IdentKind::Normal => return Ok(()), + IdentKind::Raw => "raw identifiers", + IdentKind::ForcedKeyword => "forced keywords", + } + ), + )) +} diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index aed69c9f5d938..13105ba7425fb 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -324,10 +324,13 @@ fn parse_tree<'a>( // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` // special metavariable that names the crate of the invocation. - Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => { - let (ident, kind) = token.ident().unwrap(); + Some(tokenstream::TokenTree::Token(token, _)) + if let Some((ident, kind)) = token.ident() => + { let span = ident.span.with_lo(dollar_span.lo()); - if ident.name == kw::Crate && matches!(kind, IdentKind::Normal) { + if let kw::Crate = ident.name + && let IdentKind::Normal | IdentKind::ForcedKeyword = kind + { TokenTree::token(token::Ident(kw::DollarCrate, kind), span) } else { TokenTree::MetaVar(span, ident) diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index c258d9e471079..d267bca56106c 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -25,7 +25,7 @@ use crate::diagnostics::{ }; use crate::mbe::macro_parser::NamedMatch; use crate::mbe::macro_parser::NamedMatch::*; -use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR}; +use crate::mbe::metavar_expr::{MetaVarExprConcatElem, validate_ident_kind}; use crate::mbe::{self, KleeneOp, MetaVarExpr}; /// Context needed to perform transcription of metavariable expressions. @@ -1005,21 +1005,15 @@ fn extract_symbol_from_pnr<'a>( ) -> PResult<'a, Symbol> { match pnr { ParseNtResult::Ident(nt_ident, kind) => { - if let IdentKind::Raw = kind { - Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR)) - } else { - Ok(nt_ident.name) - } + validate_ident_kind(dcx, *kind, span_err)?; + Ok(nt_ident.name) } ParseNtResult::Tt(TokenTree::Token( Token { kind: TokenKind::Ident(symbol, kind), .. }, _, )) => { - if let IdentKind::Raw = kind { - Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR)) - } else { - Ok(*symbol) - } + validate_ident_kind(dcx, *kind, span_err)?; + Ok(*symbol) } ParseNtResult::Tt(TokenTree::Token( Token { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1ecf808f78839..ae985050fe71e 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -11,7 +11,8 @@ use rustc_parse::lexer::{StripTokens, nfc_normalize}; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream}; use rustc_proc_macro::bridge::{ - DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, server, + DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, IdentKind, LitKind, Literal, Punct, + TokenTree, server, }; use rustc_proc_macro::{Delimiter, Level}; use rustc_session::Session; @@ -103,6 +104,26 @@ impl ToInternal for LitKind { } } +impl FromInternal for IdentKind { + fn from_internal(kind: tk::IdentKind) -> Self { + match kind { + tk::IdentKind::Normal => IdentKind::Normal, + tk::IdentKind::Raw => IdentKind::Raw, + tk::IdentKind::ForcedKeyword => IdentKind::ForcedKeyword, + } + } +} + +impl ToInternal for IdentKind { + fn to_internal(self) -> tk::IdentKind { + match self { + IdentKind::Normal => tk::IdentKind::Normal, + IdentKind::Raw => tk::IdentKind::Raw, + IdentKind::ForcedKeyword => tk::IdentKind::ForcedKeyword, + } + } +} + impl FromInternal for Vec> { fn from_internal(stream: TokenStream) -> Self { // Estimate the capacity as `stream.len()` rounded up to the next power @@ -127,7 +148,7 @@ impl FromInternal for Vec> { } trees.push(TokenTree::Group(Group { - delimiter: rustc_proc_macro::Delimiter::from_internal(delim), + delimiter: Delimiter::from_internal(delim), stream: Some(stream), span: DelimSpan { open: span.open, @@ -230,12 +251,12 @@ impl FromInternal for Vec> { tk::Ident(sym, kind) => trees.push(TokenTree::Ident(Ident { sym, - is_raw: matches!(kind, tk::IdentKind::Raw), + kind: IdentKind::from_internal(kind), span, })), tk::NtIdent(ident, kind) => trees.push(TokenTree::Ident(Ident { sym: ident.name, - is_raw: matches!(kind, tk::IdentKind::Raw), + kind: IdentKind::from_internal(kind), span: ident.span, })), @@ -245,7 +266,7 @@ impl FromInternal for Vec> { TokenTree::Punct(Punct { ch: b'\'', joint: true, span }), TokenTree::Ident(Ident { sym: ident.name, - is_raw: matches!(kind, tk::IdentKind::Raw), + kind: IdentKind::from_internal(kind), span, }), ]); @@ -254,7 +275,7 @@ impl FromInternal for Vec> { let stream = TokenStream::token_alone(tk::Lifetime(ident.name, kind), ident.span); trees.push(TokenTree::Group(Group { - delimiter: rustc_proc_macro::Delimiter::None, + delimiter: Delimiter::None, stream: Some(stream), span: DelimSpan::from_single(span), })) @@ -286,7 +307,7 @@ impl FromInternal for Vec> { trees.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); } trees.push(TokenTree::Group(Group { - delimiter: rustc_proc_macro::Delimiter::Bracket, + delimiter: Delimiter::Bracket, stream: Some(stream), span: DelimSpan::from_single(span), })); @@ -364,17 +385,16 @@ impl ToInternal> stream.unwrap_or_default(), )] } - TokenTree::Ident(self::Ident { sym, is_raw, span }) => { + TokenTree::Ident(Ident { sym, kind, span }) => { rustc.psess().symbol_gallery.insert(sym, span); - let kind = if is_raw { tk::IdentKind::Raw } else { tk::IdentKind::Normal }; - smallvec![tokenstream::TokenTree::token_alone(tk::Ident(sym, kind), span)] + smallvec![tokenstream::TokenTree::token_alone( + tk::Ident(sym, kind.to_internal()), + span + )] } - TokenTree::Literal(self::Literal { - kind: self::LitKind::Integer, - symbol, - suffix, - span, - }) if let Some(symbol) = symbol.as_str().strip_prefix('-') => { + TokenTree::Literal(self::Literal { kind: LitKind::Integer, symbol, suffix, span }) + if let Some(symbol) = symbol.as_str().strip_prefix('-') => + { let symbol = Symbol::intern(symbol); let integer = tk::TokenKind::lit(tk::Integer, symbol, suffix); let a = tokenstream::TokenTree::token_joint_hidden(tk::Minus, span); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 94e3b77c6b575..2b8a2629457ca 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -542,6 +542,8 @@ declare_features! ( (incomplete, fn_delegation, "1.76.0", Some(118212)), /// Traits for function pointers and items (unstable, fn_static, "CURRENT_RUSTC_VERSION", Some(148768)), + /// Allows using forced keywords `k#fn`. + (unstable, forced_keywords, "CURRENT_RUSTC_VERSION", Some(153839)), /// Allows impls for the Freeze trait. (internal, freeze_impls, "1.78.0", Some(121675)), /// Frontmatter `---` blocks for use by external tools. diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index dc6e3b1f358dc..043b7ad59d0a8 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -101,6 +101,9 @@ pub enum TokenKind { /// A raw identifier, e.g. "r#ident". RawIdent, + /// A forced keyword like `k#fn`. + ForcedKeywordIdent, + /// An unknown literal prefix, like `foo#`, `foo'`, `foo"`. Excludes /// literal prefixes that contain emoji, which are considered "invalid". /// @@ -574,7 +577,11 @@ impl<'a> Cursor<'a> { // Raw identifier, raw string literal or identifier. 'r' => match (self.first(), self.second()) { - ('#', c1) if is_id_start(c1) => self.raw_ident(), + ('#', c1) if is_id_start(c1) => { + self.bump(); // `#` + self.eat_identifier(); + RawIdent + } ('#', _) | ('"', _) => { let res = self.raw_double_quoted_string(1); let suffix_start = self.pos_within_token(); @@ -587,6 +594,16 @@ impl<'a> Cursor<'a> { _ => self.ident_or_unknown_prefix(), }, + // Forced keyword identifier or identifier. + 'k' => match (self.first(), self.second()) { + ('#', c1) if is_id_start(c1) => { + self.bump(); // `#` + self.eat_identifier(); + ForcedKeywordIdent + } + _ => self.ident_or_unknown_prefix(), + }, + // Byte literal, byte string literal, raw byte string literal or identifier. 'b' => self.c_or_byte_string( |terminated| ByteStr { terminated }, @@ -823,15 +840,6 @@ impl<'a> Cursor<'a> { Whitespace } - fn raw_ident(&mut self) -> TokenKind { - debug_assert!(self.prev() == 'r' && self.first() == '#' && is_id_start(self.second())); - // Eat "#" symbol. - self.bump(); - // Eat the identifier part of RawIdent. - self.eat_identifier(); - RawIdent - } - fn ident_or_unknown_prefix(&mut self) -> TokenKind { debug_assert!(is_id_start(self.prev())); // Start is already eaten, eat the rest of identifier. diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 566b257d1058d..4d6f37903ec60 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -2663,6 +2663,14 @@ pub(crate) struct CannotBeRawIdent { pub ident: Symbol, } +#[derive(Diagnostic)] +#[diag("`{$ident}` is not a valid keyword")] +pub(crate) struct CannotBeForcedKeywordIdent { + #[primary_span] + pub span: Span, + pub ident: Symbol, +} + #[derive(Diagnostic)] #[diag("`{$ident}` cannot be a raw lifetime")] pub(crate) struct CannotBeRawLifetime { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 787747ff5daf9..b4cdbd58e5538 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -240,6 +240,36 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.psess.raw_identifier_spans.push(span); token::Ident(sym, IdentKind::Raw) } + rustc_lexer::TokenKind::ForcedKeywordIdent => { + let span = self.mk_sp(start, self.pos); + + if span.edition().at_least_rust_2021() { + let sym = nfc_normalize(self.str_from(start + BytePos(2))); + self.psess.symbol_gallery.insert(sym, span); + if !sym.can_be_forced_keyword() { + self.dcx().emit_err(crate::diagnostics::CannotBeForcedKeywordIdent { span, ident: sym }); + } + self.psess.gated_spans.gate(sym::forced_keywords, span); + token::Ident(sym, IdentKind::ForcedKeyword) + } else { + // Reset the state so that only the `k` was consumed. + self.pos = start + BytePos(1); + self.cursor = Cursor::new(&str_before[1..], FrontmatterAllowed::No); + + self.psess.buffer_lint( + RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, + span, + ast::CRATE_NODE_ID, + crate::diagnostics::ReservedPrefixLint { + subject: "this".into(), + kind: "forced keyword", + edition: Edition::Edition2021, + sugg: self.mk_sp(start, self.pos).shrink_to_hi(), + } + ); + self.ident(start) + } + } rustc_lexer::TokenKind::UnknownPrefix => { self.report_unknown_prefix(start); self.ident(start) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index db6af6ab17aca..e67b021881e10 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -243,18 +243,19 @@ impl<'a> Parser<'a> { None }; - let suggest_remove_comma = - if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { - if recover { - self.bump(); - recovered_ident = self.ident_or_err(false).ok(); - }; - - Some(SuggRemoveComma { span: bad_token.span }) - } else { - None + let suggest_remove_comma = if self.token == token::Comma + && let Some(ident) = self.look_ahead(1, Token::ident) + { + if recover { + self.bump(); + recovered_ident = Some(ident); }; + Some(SuggRemoveComma { span: bad_token.span }) + } else { + None + }; + let help_cannot_start_number = self.is_lit_bad_ident().map(|(len, valid_portion)| { let (invalid, valid) = self.token.span.split_at(len as u32); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a2db5c047394a..8a8b56b59c142 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -741,8 +741,7 @@ impl<'a> Parser<'a> { lo: Span, ) -> PResult<'a, Box> { let mut res = loop { - let has_question = if self.prev_token == TokenKind::Ident(kw::Return, IdentKind::Normal) - { + let has_question = if self.prev_token.is_keyword(kw::Return) { // We are using noexpect here because we don't expect a `?` directly after // a `return` which could be suggested otherwise. self.eat_noexpect(&token::Question) @@ -754,7 +753,7 @@ impl<'a> Parser<'a> { e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e)); continue; } - let has_dot = if self.prev_token == TokenKind::Ident(kw::Return, IdentKind::Normal) { + let has_dot = if self.prev_token.is_keyword(kw::Return) { // We are using noexpect here because we don't expect a `.` directly after // a `return` which could be suggested otherwise. self.eat_noexpect(&token::Dot) @@ -1443,6 +1442,7 @@ impl<'a> Parser<'a> { // or `async gen {}` and `async gen move {}` // FIXME: (async) gen closures aren't yet parsed. // FIXME(gen_blocks): Parse `gen async` and suggest swap + // FIXME(forced_keywords): Allow k#gen blocks prior to Rust 2024, too! if this.token_uninterpolated_span().at_least_rust_2024() && this.is_gen_block(kw::Gen, at_async as usize) { @@ -2088,7 +2088,9 @@ impl<'a> Parser<'a> { } }; match self.token.uninterpolate().kind { - token::Ident(name, IdentKind::Normal) if name.is_bool_lit() => { + token::Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) + if name.is_bool_lit() => + { self.bump(); Some(token::Lit::new(token::Bool, name, None)) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1a641e84e96c6..e202d7833a73a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2470,8 +2470,7 @@ impl<'a> Parser<'a> { /// for better diagnostics and suggestions. fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> { let (ident, kind) = self.ident_or_err(true)?; - if kind == IdentKind::Normal - && ident.is_reserved() + if token::ident_of_kind_is_reserved(ident, kind) && !(ident.name == kw::Underscore && adt_ty == "enum") { let snapshot = self.create_snapshot_for_diagnostic(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 281ca98e97639..7e5dc439c3d10 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -456,15 +456,17 @@ impl<'a> Parser<'a> { pub(crate) fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, Ident> { let (ident, kind) = self.ident_or_err(recover)?; - if kind == IdentKind::Normal && ident.is_reserved() { + if token::ident_of_kind_is_reserved(ident, kind) { let err = self.expected_ident_found_err(); - if recover { + if recover && kind != IdentKind::ForcedKeyword { err.emit(); } else { return Err(err); } } + self.bump(); + Ok(ident) } @@ -719,7 +721,10 @@ impl<'a> Parser<'a> { self.is_keyword_ahead(0, &[kw::Const]) && self.look_ahead(1, |t| match t.uninterpolate().kind { - token::Ident(kw::Move | kw::Use | kw::Static, IdentKind::Normal) + token::Ident( + kw::Move | kw::Use | kw::Static, + IdentKind::Normal | IdentKind::ForcedKeyword, + ) | token::OrOr | token::Or => true, _ => false, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 1ab07a626d1c0..f53cfc792bd69 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -353,7 +353,7 @@ impl<'a> Parser<'a> { matches!( &token.uninterpolate().kind, token::FatArrow // e.g. `a | => 0,`. - | token::Ident(kw::If, token::IdentKind::Normal) // e.g. `a | if expr`. + | token::Ident(kw::If, token::IdentKind::Normal | token::IdentKind::ForcedKeyword) // e.g. `a | if expr`. | token::Eq // e.g. `let a | = 0`. | token::Semi // e.g. `let a |;`. | token::Colon // e.g. `let a | :`. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 098b6595f166e..241a8b2867d51 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1029,6 +1029,7 @@ symbols! { forall, forbid, force_target_feature, + forced_keywords, forget, format_args, format_args_capture, @@ -2579,6 +2580,7 @@ impl fmt::Display for Ident { pub enum IdentPrintMode { Normal, RawIdent, + ForcedKeywordIdent, RawLifetime, } @@ -2639,6 +2641,10 @@ impl fmt::Display for IdentPrinter { f.write_str("r#")?; self.symbol } + IdentPrintMode::ForcedKeywordIdent => { + f.write_str("k#")?; + self.symbol + } IdentPrintMode::RawLifetime => { f.write_str("'r#")?; let s = self @@ -3074,6 +3080,11 @@ impl Symbol { self != sym::empty && self != kw::Underscore && !self.is_path_segment_keyword() } + /// Returns `true` if this symbol can be a forced keyword. + pub fn can_be_forced_keyword(self) -> bool { + self.is_reserved(|| Edition::EditionFuture) || self.is_weak() + } + /// Was this symbol index predefined in the compiler's `symbols!` macro? /// Note: this applies to both `Symbol`s and `ByteSymbol`s, which is why it /// takes a `u32` argument instead of a `&self` argument. Use with care. diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 0761549b2e30d..4b12ed0058847 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -222,6 +222,7 @@ mark_noop! { usize, Delimiter, LitKind, + IdentKind, Level, Bound, Range, @@ -385,11 +386,36 @@ compound_traits!(struct Punct { ch, joint, span }); #[derive(Copy, Clone, Eq, PartialEq)] pub struct Ident { pub sym: Symbol, - pub is_raw: bool, + pub kind: IdentKind, pub span: Span, } -compound_traits!(struct Ident { sym, is_raw, span }); +compound_traits!(struct Ident { sym, kind, span }); + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum IdentKind { + Normal, + Raw, + ForcedKeyword, +} + +impl IdentKind { + pub fn prefix(self) -> Option<&'static str> { + match self { + Self::Normal => None, + Self::Raw => Some("r#"), + Self::ForcedKeyword => Some("k#"), + } + } +} + +rpc_encode_decode!( + enum IdentKind { + Normal, + Raw, + ForcedKeyword, + } +); #[derive(Clone, Eq, PartialEq)] pub struct Literal { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b8ffcd53b25d8..e7519a8c018f2 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1197,7 +1197,7 @@ impl Ident { pub fn new(string: &str, span: Span) -> Ident { Ident(bridge::Ident { sym: bridge::client::Symbol::new_ident(string, false), - is_raw: false, + kind: bridge::IdentKind::Normal, span: span.0, }) } @@ -1210,7 +1210,7 @@ impl Ident { pub fn new_raw(string: &str, span: Span) -> Ident { Ident(bridge::Ident { sym: bridge::client::Symbol::new_ident(string, true), - is_raw: true, + kind: bridge::IdentKind::Raw, span: span.0, }) } @@ -1234,8 +1234,8 @@ impl Ident { #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0.is_raw { - f.write_str("r#")?; + if let Some(prefix) = self.0.kind.prefix() { + f.write_str(prefix)?; } fmt::Display::fmt(&self.0.sym, f) } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 9c73e3b4ad687..6ef5de0b20aba 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -1297,6 +1297,7 @@ impl<'src> Classifier<'src> { TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => { Class::Ident(new_span(before, text, file_span)) } + TokenKind::ForcedKeywordIdent => Class::KeyWord, TokenKind::Lifetime { .. } | TokenKind::RawLifetime | TokenKind::UnknownPrefixLifetime => Class::Lifetime, diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index ec994b731bf95..f449809911c90 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -246,6 +246,8 @@ impl<'a> Converter<'a> { } rustc_lexer::TokenKind::RawIdent => IDENT, + #[cfg(feature = "in-rust-tree")] + rustc_lexer::TokenKind::ForcedKeywordIdent => ERROR, rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => { // FIXME: rustc does something better for recovery. diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index b9b6247b54fab..55ccc98f4f23e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -699,14 +699,17 @@ impl<'a, T: SpanTransformer> proc_macro_srv::TokenTree::Ident(ident) => { let idx = self.ident.len() as u32; let id = self.token_id_of(ident.span); + // NOTE(forced_keywords): Let's not bother to preserve forced keywords in this + // legacy protocol. + let is_raw = matches!(ident.kind, proc_macro_srv::IdentKind::Raw); let text = if self.version >= EXTENDED_LEAF_DATA { self.intern(ident.sym.as_str()) - } else if ident.is_raw { + } else if is_raw { self.intern_owned(format!("r#{}", ident.sym.as_str(),)) } else { self.intern(ident.sym.as_str()) }; - self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw }); + self.ident.push(IdentRepr { id, text, is_raw }); (idx << 2) | 0b11 } }; @@ -944,7 +947,10 @@ impl Reader<'_, T> { proc_macro_srv::TokenTree::Ident(proc_macro_srv::Ident { sym: Symbol::intern(text), span: read_span(repr.id), - is_raw: is_raw.yes(), + kind: match is_raw { + tt::IdentIsRaw::Yes => proc_macro_srv::IdentKind::Raw, + tt::IdentIsRaw::No => proc_macro_srv::IdentKind::Normal, + }, }) } other => panic!("bad tag: {other}"), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs index fc62f9413a34e..e573e57c8b7b1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/bridge.rs @@ -2,7 +2,7 @@ use rustc_proc_macro::bridge as pm_bridge; -pub use pm_bridge::{DelimSpan, Diagnostic, ExpnGlobals, LitKind}; +pub use pm_bridge::{DelimSpan, Diagnostic, ExpnGlobals, IdentKind, LitKind}; pub type TokenTree = pm_bridge::TokenTree, S, intern::Symbol>; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs index 5201bb6aeb86b..7038152ac2651 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs @@ -7,7 +7,7 @@ use intern::Symbol; use rustc_lexer::{DocStyle, LiteralKind}; use rustc_proc_macro::Delimiter; -use crate::bridge::{DelimSpan, Group, Ident, LitKind, Literal, Punct, TokenTree}; +use crate::bridge::{DelimSpan, Group, Ident, IdentKind, LitKind, Literal, Punct, TokenTree}; /// Trait for allowing tests to parse tokenstreams with dynamic span ranges pub(crate) trait SpanLike { @@ -200,7 +200,7 @@ impl TokenStream { stream: Some(TokenStream::new(vec![ TokenTree::Ident(Ident { sym: Symbol::intern("doc"), - is_raw: false, + kind: IdentKind::Normal, span, }), TokenTree::Punct(Punct { ch: b'=', joint: false, span }), @@ -226,7 +226,7 @@ impl TokenStream { stream: Some(TokenStream::new(vec![ TokenTree::Ident(Ident { sym: Symbol::intern("doc"), - is_raw: false, + kind: IdentKind::Normal, span, }), TokenTree::Punct(Punct { ch: b'=', joint: false, span }), @@ -267,7 +267,7 @@ impl TokenStream { } rustc_lexer::TokenKind::Ident => tokenstream.push(TokenTree::Ident(Ident { sym: Symbol::intern(&s[range.clone()]), - is_raw: false, + kind: IdentKind::Normal, span: span.derive_ranged(range), })), rustc_lexer::TokenKind::InvalidIdent => { @@ -277,7 +277,15 @@ impl TokenStream { let range = range.start + 2..range.end; tokenstream.push(TokenTree::Ident(Ident { sym: Symbol::intern(&s[range.clone()]), - is_raw: true, + kind: IdentKind::Raw, + span: span.derive_ranged(range), + })) + } + rustc_lexer::TokenKind::ForcedKeywordIdent => { + let range = range.start + 2..range.end; + tokenstream.push(TokenTree::Ident(Ident { + sym: Symbol::intern(&s[range.clone()]), + kind: IdentKind::ForcedKeyword, span: span.derive_ranged(range), })) } @@ -298,7 +306,7 @@ impl TokenStream { })); tokenstream.push(TokenTree::Ident(Ident { sym: Symbol::intern(&s[range.clone()]), - is_raw: true, + kind: IdentKind::Raw, span: span.derive_ranged(range), })) } @@ -314,7 +322,7 @@ impl TokenStream { })); tokenstream.push(TokenTree::Ident(Ident { sym: Symbol::intern(&s[range.clone()]), - is_raw: false, + kind: IdentKind::Normal, span: span.derive_ranged(range), })) } @@ -484,9 +492,9 @@ fn display_token_tree( *emit_whitespace = !*joint; write!(f, "{}", *ch as char)?; } - TokenTree::Ident(Ident { sym, is_raw, span: _ }) => { - if *is_raw { - write!(f, "r#")?; + TokenTree::Ident(Ident { sym, kind, span: _ }) => { + if let Some(prefix) = kind.prefix() { + write!(f, "{prefix}")?; } write!(f, "{sym}")?; *emit_whitespace = true; @@ -613,10 +621,10 @@ fn debug_token_tree( *ch as char, if *joint { "[joint]" } else { "[alone]" } )?, - TokenTree::Ident(Ident { sym, is_raw, span }) => { + TokenTree::Ident(Ident { sym, kind, span }) => { write!(f, "IDENT {span:#?} ")?; - if *is_raw { - write!(f, "r#")?; + if let Some(prefix) = kind.prefix() { + write!(f, "{prefix}")?; } write!(f, "{sym}")?; } diff --git a/tests/ui/parser/forced-keywords/basic.rs b/tests/ui/parser/forced-keywords/basic.rs new file mode 100644 index 0000000000000..7a547b8f3d667 --- /dev/null +++ b/tests/ui/parser/forced-keywords/basic.rs @@ -0,0 +1,23 @@ +//@ edition: 2021.. +//@ check-pass +#![feature(forced_keywords)] + +k#mod module { + k#pub(k#in k#super) k#static _DATA: i32 = 0i8 k#as k#_; +} + +k#use ::std::process::Termination; + +k#fn main() -> k#impl k#self::Termination { + k#let k#true = k#false k#else { k#return }; + + k#let k#ref k#mut _x: (); + + k#const k#fn perform() -> k#impl Sized { + k#loop { + k#break k#match () { () k#if k#true => {} k#_ => {} }; + } + } + + perform(); +} diff --git a/tests/ui/parser/forced-keywords/feature-gate-forced-keywords.rs b/tests/ui/parser/forced-keywords/feature-gate-forced-keywords.rs new file mode 100644 index 0000000000000..6b557be5a289f --- /dev/null +++ b/tests/ui/parser/forced-keywords/feature-gate-forced-keywords.rs @@ -0,0 +1,6 @@ +//@ edition: 2021.. + +#[cfg(false)] +k#fn start() {} //~ ERROR forced keywords are experimental + +k#fn main() {} //~ ERROR forced keywords are experimental diff --git a/tests/ui/parser/forced-keywords/feature-gate-forced-keywords.stderr b/tests/ui/parser/forced-keywords/feature-gate-forced-keywords.stderr new file mode 100644 index 0000000000000..aeada90b64935 --- /dev/null +++ b/tests/ui/parser/forced-keywords/feature-gate-forced-keywords.stderr @@ -0,0 +1,23 @@ +error[E0658]: forced keywords are experimental + --> $DIR/feature-gate-forced-keywords.rs:4:1 + | +LL | k#fn start() {} + | ^^^^ + | + = note: see issue #153839 for more information + = help: add `#![feature(forced_keywords)]` 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]: forced keywords are experimental + --> $DIR/feature-gate-forced-keywords.rs:6:1 + | +LL | k#fn main() {} + | ^^^^ + | + = note: see issue #153839 for more information + = help: add `#![feature(forced_keywords)]` 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: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/forced-keywords/invalid-keyword.rs b/tests/ui/parser/forced-keywords/invalid-keyword.rs new file mode 100644 index 0000000000000..07339306504ab --- /dev/null +++ b/tests/ui/parser/forced-keywords/invalid-keyword.rs @@ -0,0 +1,6 @@ +//@ edition: 2021.. +#![feature(forced_keywords)] + +const _: () = k#not_a_keyword(); +//~^ ERROR `not_a_keyword` is not a valid keyword +//~| ERROR expected expression, found `k#not_a_keyword` diff --git a/tests/ui/parser/forced-keywords/invalid-keyword.stderr b/tests/ui/parser/forced-keywords/invalid-keyword.stderr new file mode 100644 index 0000000000000..9dc17e6b3e3bf --- /dev/null +++ b/tests/ui/parser/forced-keywords/invalid-keyword.stderr @@ -0,0 +1,14 @@ +error: `not_a_keyword` is not a valid keyword + --> $DIR/invalid-keyword.rs:4:15 + | +LL | const _: () = k#not_a_keyword(); + | ^^^^^^^^^^^^^^^ + +error: expected expression, found `k#not_a_keyword` + --> $DIR/invalid-keyword.rs:4:15 + | +LL | const _: () = k#not_a_keyword(); + | ^^^^^^^^^^^^^^^ expected expression + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/forced-keywords/pre-2021-edition-fail.rs b/tests/ui/parser/forced-keywords/pre-2021-edition-fail.rs new file mode 100644 index 0000000000000..77d2712802548 --- /dev/null +++ b/tests/ui/parser/forced-keywords/pre-2021-edition-fail.rs @@ -0,0 +1,3 @@ +//@ edition: 2015..2021 + +k#fn main() {} //~ ERROR expected one of `!` or `::`, found `#` diff --git a/tests/ui/parser/forced-keywords/pre-2021-edition-fail.stderr b/tests/ui/parser/forced-keywords/pre-2021-edition-fail.stderr new file mode 100644 index 0000000000000..fefc3cafd2242 --- /dev/null +++ b/tests/ui/parser/forced-keywords/pre-2021-edition-fail.stderr @@ -0,0 +1,8 @@ +error: expected one of `!` or `::`, found `#` + --> $DIR/pre-2021-edition-fail.rs:3:2 + | +LL | k#fn main() {} + | ^ expected one of `!` or `::` + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/forced-keywords/pre-2021-edition-pass.rs b/tests/ui/parser/forced-keywords/pre-2021-edition-pass.rs new file mode 100644 index 0000000000000..63cf647a5c4c1 --- /dev/null +++ b/tests/ui/parser/forced-keywords/pre-2021-edition-pass.rs @@ -0,0 +1,14 @@ +//@ edition: 2015..2021 +//@ check-pass + +#![warn(rust_2021_prefixes_incompatible_syntax)] + +macro_rules! ensure { + ($tag:ident # $name:ident) => {}; +} + +ensure! { k#fn } +//~^ WARNING parsed as a forced keyword in Rust 2021 and onward +//~| WARNING this changes meaning in Rust 2021 + +fn main() {} diff --git a/tests/ui/parser/forced-keywords/pre-2021-edition-pass.stderr b/tests/ui/parser/forced-keywords/pre-2021-edition-pass.stderr new file mode 100644 index 0000000000000..f033a56ad49ae --- /dev/null +++ b/tests/ui/parser/forced-keywords/pre-2021-edition-pass.stderr @@ -0,0 +1,20 @@ +warning: this is parsed as a forced keyword in Rust 2021 and onward + --> $DIR/pre-2021-edition-pass.rs:10:11 + | +LL | ensure! { k#fn } + | ^^^^ + | + = warning: this changes meaning in Rust 2021 + = note: for more information, see +note: the lint level is defined here + --> $DIR/pre-2021-edition-pass.rs:4:9 + | +LL | #![warn(rust_2021_prefixes_incompatible_syntax)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider inserting whitespace here to avoid this + | +LL | ensure! { k #fn } + | + + +warning: 1 warning emitted + From 01d0ca661a7c61213ff97c7632a42b3f7002f008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 17:22:59 +0200 Subject: [PATCH 2/8] ------------------------- BRANCH SEPARATOR ------------------------- From 0cfde7119566ac475a5c345535bffcbc62a3a493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 14:18:54 +0200 Subject: [PATCH 3/8] Replace `builtin # field_of` with `k#field_of` --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/token.rs | 6 +++ compiler/rustc_ast_pretty/src/pprust/state.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 38 +++++++++---------- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/field.rs | 2 +- library/core/src/lib.rs | 1 + .../invalid.next.stderr | 6 +-- .../invalid.old.stderr | 6 +-- tests/ui/field_representing_types/invalid.rs | 8 ++-- 10 files changed, 40 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5b9f3231fc744..be0b01fa94be1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2572,7 +2572,7 @@ pub enum TyKind { /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. Pat(Box, Box), - /// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`). + /// A `field_of` expression (e.g., `k#field_of(Struct, field)`). /// /// Usually not written directly in user code but indirectly via the macro /// `core::field::field_of!(...)`. diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index ee693e66eb2e5..b4558a47b7e40 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -350,6 +350,8 @@ fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `ty` nonterminal which is user observable. + // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + let ident_token = Token::new(Ident(name, kind), span); !ident_token.is_reserved_ident() @@ -936,6 +938,10 @@ impl Token { self.non_raw_ident().is_some_and(|id| id.name == kw) } + pub fn is_forced_keyword(&self, kw: Symbol) -> bool { + self.ident().is_some_and(|(id, kind)| id.name == kw && kind == IdentKind::ForcedKeyword) + } + /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this /// token is an identifier equal to `kw` ignoring the case. pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 73242f521e182..87ddcfa6db39f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1438,7 +1438,7 @@ impl<'a> State<'a> { self.print_ty_pat(pat); } ast::TyKind::FieldOf(ty, variant, field) => { - self.word("builtin # field_of"); + self.word("k#field_of"); self.popen(); let ib = self.ibox(0); self.print_type(ty); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 433abcc47e2b0..43d3cc7c40e3b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -306,8 +306,6 @@ impl<'a> Parser<'a> { self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty(lo)? - } else if self.is_builtin() { - self.parse_builtin_ty()? } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer @@ -402,6 +400,9 @@ impl<'a> Parser<'a> { && self.look_ahead(1, |tok| tok.kind == token::Lt) { self.parse_unsafe_binder_ty()? + } else if self.token.is_forced_keyword(kw::FieldOf) { + self.bump(); + self.parse_ty_field_of(lo)? } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.dcx().struct_span_err(lo, msg); @@ -795,16 +796,9 @@ impl<'a> Parser<'a> { Ok(TyKind::Err(guar)) } - fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { - self.parse_builtin(|this, lo, ident| { - Ok(match ident.name { - sym::field_of => Some(this.parse_ty_field_of(lo)?), - _ => None, - }) - }) - } + pub(crate) fn parse_ty_field_of(&mut self, lo: Span) -> PResult<'a, TyKind> { + self.expect(exp!(OpenParen))?; - pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { let container = self.parse_ty()?; self.expect(exp!(Comma))?; @@ -813,30 +807,36 @@ impl<'a> Parser<'a> { if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { if trailing_comma { - e.note("unexpected third argument to field_of"); + e.note("unexpected third argument to `field_of`"); } else { - e.note("field_of expects dot-separated field and variant names"); + e.note("`field_of` expects dot-separated field and variant names"); } e.emit(); } - // Eat tokens until the macro call ends. + // Eat tokens until the construct ends. if self.may_recover() { while !self.token.kind.is_close_delim_or_eof() { self.bump(); } } + // FIXME: Odd not include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly. + let span = self.token.span; + self.expect(exp!(CloseParen))?; + + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + match *fields { - [] => Err(self.dcx().struct_span_err( - self.token.span, - "`field_of!` expects dot-separated field and variant names", - )), + [] => Err(self + .dcx() + .struct_span_err(span, "`field_of` expects dot-separated field and variant names")), [field] => Ok(TyKind::FieldOf(container, None, field)), [variant, field] => Ok(TyKind::FieldOf(container, Some(variant), field)), _ => Err(self.dcx().struct_span_err( fields.iter().map(|f| f.span).collect::>(), - "`field_of!` only supports a single field or a variant with a field", + "`field_of` only supports a single field or a variant with a field", )), } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 241a8b2867d51..450bb0edcbb65 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -129,6 +129,7 @@ symbols! { ContractEnsures: "contract_ensures", ContractRequires: "contract_requires", Default: "default", + FieldOf: "field_of", MacroRules: "macro_rules", Pin: "pin", Raw: "raw", @@ -985,7 +986,6 @@ symbols! { field, field_base, field_init_shorthand, - field_of, field_offset, field_projections, field_representing_type, diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 5a8ae7759bc1e..07c8c991bac43 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -134,7 +134,7 @@ impl Ord // it to `FieldRepresentingType<...>`. Thus stabilizing this requires careful thought about the // completeness of the trait impls for `FieldRepresentingType`. pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { - builtin # field_of($Container, $($fields)+) + k#field_of($Container, $($fields)+) } /// Type representing a field of a `struct`, `union`, `enum` variant or tuple. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d7d8d32ed88e7..48f15e602097a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -129,6 +129,7 @@ #![feature(f128)] #![feature(field_projections)] #![feature(final_associated_functions)] +#![feature(forced_keywords)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] diff --git a/tests/ui/field_representing_types/invalid.next.stderr b/tests/ui/field_representing_types/invalid.next.stderr index 38b011ab6f84a..3a5753b558cca 100644 --- a/tests/ui/field_representing_types/invalid.next.stderr +++ b/tests/ui/field_representing_types/invalid.next.stderr @@ -33,7 +33,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Enum, Variant..field); | ^^^^^^^^^^^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:27:12 | LL | let _: field_of!(Enum, Variant..field); @@ -48,7 +48,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Struct, [42]); | ^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:29:12 | LL | let _: field_of!(Struct, [42]); @@ -57,7 +57,7 @@ LL | let _: field_of!(Struct, [42]); | in this macro invocation | this macro call doesn't expand to a type -error: `field_of!` only supports a single field or a variant with a field +error: `field_of` only supports a single field or a variant with a field --> $DIR/invalid.rs:31:30 | LL | let _: field_of!(Struct, field1.field2.field3); diff --git a/tests/ui/field_representing_types/invalid.old.stderr b/tests/ui/field_representing_types/invalid.old.stderr index 38b011ab6f84a..3a5753b558cca 100644 --- a/tests/ui/field_representing_types/invalid.old.stderr +++ b/tests/ui/field_representing_types/invalid.old.stderr @@ -33,7 +33,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Enum, Variant..field); | ^^^^^^^^^^^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:27:12 | LL | let _: field_of!(Enum, Variant..field); @@ -48,7 +48,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Struct, [42]); | ^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:29:12 | LL | let _: field_of!(Struct, [42]); @@ -57,7 +57,7 @@ LL | let _: field_of!(Struct, [42]); | in this macro invocation | this macro call doesn't expand to a type -error: `field_of!` only supports a single field or a variant with a field +error: `field_of` only supports a single field or a variant with a field --> $DIR/invalid.rs:31:30 | LL | let _: field_of!(Struct, field1.field2.field3); diff --git a/tests/ui/field_representing_types/invalid.rs b/tests/ui/field_representing_types/invalid.rs index d1fc217db7ddd..bdc9137ccf793 100644 --- a/tests/ui/field_representing_types/invalid.rs +++ b/tests/ui/field_representing_types/invalid.rs @@ -23,10 +23,10 @@ fn main() { let _: field_of!(Struct); //~ ERROR: unexpected end of macro invocation let _: field_of!(Struct,); //~ ERROR: unexpected end of macro invocation let _: field_of!(Struct, field, extra); //~ ERROR: no rules expected `extra` - // FIXME(FRTs): adjust error message to mention `field_of!` & prevent double errors + // FIXME(FRTs): adjust error message to mention `field_of` & prevent double errors let _: field_of!(Enum, Variant..field); //~ ERROR: offset_of expects dot-separated field and variant names - //~^ ERROR: `field_of!` expects dot-separated field and variant names + //~^ ERROR: `field_of` expects dot-separated field and variant names let _: field_of!(Struct, [42]); //~ ERROR: offset_of expects dot-separated field and variant names - //~^ ERROR: `field_of!` expects dot-separated field and variant names - let _: field_of!(Struct, field1.field2.field3); //~ ERROR: `field_of!` only supports a single field or a variant with a field + //~^ ERROR: `field_of` expects dot-separated field and variant names + let _: field_of!(Struct, field1.field2.field3); //~ ERROR: `field_of` only supports a single field or a variant with a field } From 67da9c8cef4bdeec1d88448b03dd7f6b16d4cf2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 12:27:18 +0200 Subject: [PATCH 4/8] Replace `builtin # deref` with `k#deref` --- compiler/rustc_ast_pretty/src/pprust/state.rs | 2 +- compiler/rustc_attr_ir/src/lang_items.rs | 2 +- compiler/rustc_hir_typeck/src/place_op.rs | 4 +-- .../src/builder/matches/test.rs | 4 +-- compiler/rustc_parse/src/parser/pat.rs | 31 +++++++------------ compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/macros/mod.rs | 2 +- .../src/methods/should_implement_trait.rs | 2 +- tests/ui/unpretty/exhaustive.expanded.stdout | 2 +- 9 files changed, 22 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 87ddcfa6db39f..f28a126d534f4 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2008,7 +2008,7 @@ impl<'a> State<'a> { self.pclose(); } PatKind::Deref(inner) => { - self.word("deref!"); + self.word("k#deref"); self.popen(); self.print_pat(inner); self.pclose(); diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index 60890f7799290..efc2e181d0972 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -238,7 +238,7 @@ language_item_table! { Complex, sym::complex, complex, Target::Struct, GenericRequirement::Exact(1); - Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); + Deref, kw::Deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefPure, sym::deref_pure, deref_pure_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy(AssocCtxt::Trait), GenericRequirement::None; diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index f3cba3a27d69f..7ccd7eb435d03 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::adjustment::{ OverloadedDeref, PointerCoercion, }; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, span_bug, sym}; +use rustc_span::{Span, kw, span_bug, sym}; use tracing::debug; use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; @@ -203,7 +203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); let (Some(imm_tr), imm_op) = (match op { - PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), + PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), kw::Deref), PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), }) else { // Bail if `Deref` or `Index` isn't defined. diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 2e4d80d6d140d..96108136d7a36 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; use rustc_span::def_id::DefId; -use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, bug, sym}; +use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, bug, kw, sym}; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -343,7 +343,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, ) { let (trait_item, method) = match mutability { - Mutability::Not => (LangItem::Deref, sym::deref), + Mutability::Not => (LangItem::Deref, kw::Deref), Mutability::Mut => (LangItem::DerefMut, sym::deref_mut), }; let borrow_kind = super::util::ref_pat_borrow_kind(mutability); diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index f53cfc792bd69..c8c4182486422 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -801,8 +801,18 @@ impl<'a> Parser<'a> { } else { PatKind::Expr(const_expr) } - } else if self.is_builtin() { - self.parse_pat_builtin()? + } else if self.token.is_forced_keyword(kw::Deref) { + self.bump(); + self.expect(exp!(OpenParen))?; + let pat = ast::PatKind::Deref(Box::new(self.parse_pat_allow_top_guard( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?)); + self.expect(exp!(CloseParen))?; + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + pat } // Don't eagerly error on semantically invalid tokens when matching // declarative macros, as the input to those doesn't have to be @@ -1608,23 +1618,6 @@ impl<'a> Parser<'a> { .contains(&self.token.kind) } - fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> { - self.parse_builtin(|self_, _lo, ident| { - Ok(match ident.name { - // builtin#deref(PAT) - sym::deref => { - Some(ast::PatKind::Deref(Box::new(self_.parse_pat_allow_top_guard( - None, - RecoverComma::Yes, - RecoverColon::Yes, - CommaRecoveryMode::LikelyTuple, - )?))) - } - _ => None, - }) - }) - } - // FIXME: remove this entirely eventually /// Parses the removed `box pat` syntax to provide a more helpful error message. fn parse_pat_box(&mut self) -> PResult<'a, PatKind> { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 450bb0edcbb65..6decb3069ddaa 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -129,6 +129,7 @@ symbols! { ContractEnsures: "contract_ensures", ContractRequires: "contract_requires", Default: "default", + Deref: "deref", FieldOf: "field_of", MacroRules: "macro_rules", Pin: "pin", @@ -823,7 +824,6 @@ symbols! { deprecated, deprecated_safe, deprecated_suggestion, - deref, deref_method, deref_mut, deref_patterns, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index ab8fc47009186..3e7b404aedbc9 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1927,7 +1927,7 @@ pub(crate) mod builtin { )] #[diagnostic::opaque] pub macro deref($pat:pat) { - builtin # deref($pat) + k#deref($pat) } /// Derive macro generating an impl of the trait `From`. diff --git a/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs b/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs index 9e0d630461915..f9e5c432a2f5d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs +++ b/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs @@ -112,7 +112,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, SelfKind::Ref, OutType::Any, true, Edition2015), ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, SelfKind::Ref, OutType::Any, true, Edition2015), ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, SelfKind::No, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", kw::Deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, SelfKind::Value, OutType::Any, true, Edition2015), ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, SelfKind::RefMut, OutType::Unit, true, Edition2015), diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 6dd7b4c5e1f3a..a1bafc2ed9d22 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -513,7 +513,7 @@ mod patterns { /// PatKind::Tuple fn pat_tuple() { let (); let (true,); let (true, false); } /// PatKind::Deref - fn pat_deref() { let deref!(pat); } + fn pat_deref() { let k#deref(pat); } /// PatKind::Ref fn pat_ref() { let &pat; let &mut pat; } /// PatKind::Expr From 8671cb2d991bf0f48fc7baee44ebff2f6c2550d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 13:00:27 +0200 Subject: [PATCH 5/8] Replace `builtin # offset_of` with `k#offset_of` --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/token.rs | 2 + .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- compiler/rustc_attr_ir/src/lang_items.rs | 2 +- .../src/interpret/intrinsics.rs | 4 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 6 +- compiler/rustc_parse/src/parser/expr.rs | 13 +- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/mem/mod.rs | 4 +- .../feature-gate-builtin_syntax.rs | 5 +- .../feature-gate-builtin_syntax.stderr | 6 +- tests/ui/offset-of/offset-of-builtin.rs | 47 +++---- tests/ui/offset-of/offset-of-builtin.stderr | 131 ++++++++++++++---- tests/ui/offset-of/offset-of-tuple-field.rs | 11 +- .../ui/offset-of/offset-of-tuple-field.stderr | 54 ++++---- tests/ui/offset-of/offset-of-tuple.rs | 24 ++-- tests/ui/offset-of/offset-of-tuple.stderr | 86 ++++++------ tests/ui/unpretty/exhaustive.expanded.stdout | 2 +- 18 files changed, 241 insertions(+), 162 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index be0b01fa94be1..f32dd7c260478 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1864,7 +1864,7 @@ pub enum ExprKind { /// Output of the `asm!()` macro. InlineAsm(Box), - /// An `offset_of` expression (e.g., `builtin # offset_of(Struct, field)`). + /// An `offset_of` expression (e.g., `k#offset_of(Struct, field)`). /// /// Usually not written directly in user code but /// indirectly via the macro `core::mem::offset_of!(...)`. diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index b4558a47b7e40..95f3d91f2df47 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -313,6 +313,8 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `expr` nonterminal which is user observable. + // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + let ident_token = Token::new(Ident(name, kind), span); // FIXME: Remove `box` from this list given we officially no longer support box expressions diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index b6c22e7da9cb1..d84024205404c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -795,7 +795,7 @@ impl<'a> State<'a> { self.pclose(); } ast::ExprKind::OffsetOf(container, fields) => { - self.word("builtin # offset_of"); + self.word("k#offset_of"); self.popen(); let ib = self.ibox(0); self.print_type(container); diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index efc2e181d0972..e76acd18e5bab 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -163,7 +163,7 @@ language_item_table! { Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); AlignOf, sym::mem_align_const, align_const, Target::AssocConst(AssocCtxt::Trait), GenericRequirement::Exact(0); SizeOf, sym::mem_size_const, size_const, Target::AssocConst(AssocCtxt::Trait), GenericRequirement::Exact(0); - OffsetOf, sym::offset_of, offset_of, Target::Fn, GenericRequirement::Exact(1); + OffsetOf, kw::OffsetOf, offset_of, Target::Fn, GenericRequirement::Exact(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b8b372ad1dab1..6e868bb15e1e9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -15,7 +15,7 @@ use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{FloatTy, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::{Symbol, bug, span_bug, sym}; +use rustc_span::{Symbol, bug, kw, span_bug, sym}; use tracing::trace; use super::memory::MemoryKind; @@ -232,7 +232,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let val = layout.align.bytes(); self.write_scalar(Scalar::from_target_usize(val, self), dest)?; } - sym::offset_of => { + kw::OffsetOf => { let tp_ty = instance.args.type_at(0); let variant = self.read_scalar(&args[0])?.to_u32()?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index b566f6b04bf41..f3a91fe4149cd 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -7,7 +7,7 @@ use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol, kw, sym}; use crate::check::check_function_signature; use crate::diagnostics::{UnrecognizedIntrinsicFunction, WrongNumberOfGenericArgumentsToIntrinsic}; @@ -154,7 +154,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::non_exhaustive | sym::offload | sym::offload_get_num_devices - | sym::offset_of + | kw::OffsetOf | sym::overflow_checks | sym::powf16 | sym::powf32 @@ -298,7 +298,7 @@ pub(crate) fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), - sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), + kw::OffsetOf => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8a8b56b59c142..e31dedb8dc431 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1435,6 +1435,9 @@ impl<'a> Parser<'a> { return Ok(expr); } Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) + } else if this.token.is_forced_keyword(kw::OffsetOf) { + this.bump(); + this.parse_expr_offset_of(lo) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. let at_async = this.check_keyword(exp!(Async)); @@ -1879,7 +1882,6 @@ impl<'a> Parser<'a> { fn parse_expr_builtin(&mut self) -> PResult<'a, Box> { self.parse_builtin(|this, lo, ident| { Ok(match ident.name { - sym::offset_of => Some(this.parse_expr_offset_of(lo)?), sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), sym::wrap_binder => { Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) @@ -1925,8 +1927,9 @@ impl<'a> Parser<'a> { ret } - /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, Box> { + self.expect(exp!(OpenParen))?; + let container = self.parse_ty()?; self.expect(exp!(Comma))?; @@ -1949,7 +1952,13 @@ impl<'a> Parser<'a> { } } + // FIXME: Odd not include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly. let span = lo.to(self.token.span); + self.expect(exp!(CloseParen))?; + + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6decb3069ddaa..aef3a336b5688 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -132,6 +132,7 @@ symbols! { Deref: "deref", FieldOf: "field_of", MacroRules: "macro_rules", + OffsetOf: "offset_of", Pin: "pin", Raw: "raw", Reuse: "reuse", @@ -1495,7 +1496,6 @@ symbols! { offload_get_num_devices, offload_kernel, offset, - offset_of, offset_of_enum, offset_of_nested, offset_of_slice, diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9d871d8c5745d..13c61887827e0 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1664,10 +1664,10 @@ impl SizedTypeProperties for T {} note = "this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`" )] #[doc(alias = "memoffset")] -#[allow_internal_unstable(builtin_syntax, core_intrinsics)] +#[allow_internal_unstable(core_intrinsics, builtin_syntax)] #[diagnostic::opaque] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { - const { builtin # offset_of($Container, $($fields)+) } + const { k#offset_of($Container, $($fields)+) } } /// Create a fresh instance of the inhabited ZST type `T`. diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs b/tests/ui/feature-gates/feature-gate-builtin_syntax.rs index 832bb5a96bc3d..e36ffe1503956 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs +++ b/tests/ui/feature-gates/feature-gate-builtin_syntax.rs @@ -1,7 +1,10 @@ +//@ edition: 2021.. +#![feature(forced_keywords)] + struct Foo { v: u8, w: u8, } fn main() { - builtin # offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable + k#offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable } diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr b/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr index 297363b3de711..59a39df794c44 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr +++ b/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr @@ -1,8 +1,8 @@ error[E0658]: `builtin #` syntax is unstable - --> $DIR/feature-gate-builtin_syntax.rs:6:15 + --> $DIR/feature-gate-builtin_syntax.rs:9:5 | -LL | builtin # offset_of(Foo, v); - | ^^^^^^^^^ +LL | k#offset_of(Foo, v); + | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #110680 for more information = help: add `#![feature(builtin_syntax)]` to the crate attributes to enable diff --git a/tests/ui/offset-of/offset-of-builtin.rs b/tests/ui/offset-of/offset-of-builtin.rs index 6664c10f905ef..dbd5495cf5464 100644 --- a/tests/ui/offset-of/offset-of-builtin.rs +++ b/tests/ui/offset-of/offset-of-builtin.rs @@ -1,32 +1,23 @@ -#![feature(builtin_syntax)] +//@ edition: 2021.. +#![feature(forced_keywords, builtin_syntax)] -// For the exposed macro we already test these errors in the other files, -// but this test helps to make sure the builtin construct also errors. -// This has the same examples as offset-of-arg-count.rs +use std::mem::offset_of; fn main() { - builtin # offset_of(NotEnoughArguments); //~ ERROR expected one of -} -fn t1() { - builtin # offset_of(NotEnoughArgumentsWithAComma, ); //~ ERROR expected expression -} -fn t2() { - builtin # offset_of(S, f, too many arguments); //~ ERROR expected `)`, found `too` -} -fn t3() { - builtin # offset_of(S, f); // compiles fine -} -fn t4() { - builtin # offset_of(S, f.); //~ ERROR unexpected token -} -fn t5() { - builtin # offset_of(S, f.,); //~ ERROR unexpected token -} -fn t6() { - builtin # offset_of(S, f..); //~ ERROR offset_of expects dot-separated field and variant names -} -fn t7() { - builtin # offset_of(S, f..,); //~ ERROR offset_of expects dot-separated field and variant names -} + offset_of!((u8, u8), _0); //~ ERROR no field `_0` + offset_of!((u8, u8), 01); //~ ERROR no field `01` + offset_of!((u8, u8), 1e2); //~ ERROR no field `1e2` + offset_of!((u8, u8), 1_u8); //~ ERROR no field `1_` + //~| ERROR suffixes on a tuple index -struct S { f: u8, } + k#offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` + k#offset_of((u8, u8), _0); //~ ERROR no field `_0` + k#offset_of((u8, u8), 01); //~ ERROR no field `01` + k#offset_of((u8, u8), 1_u8); //~ ERROR no field `1_` + //~| ERROR suffixes on a tuple index + + offset_of!(((u8, u16), (u32, u16, u8)), 0.2); //~ ERROR no field `2` + offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); //~ ERROR no field `1e2` + offset_of!(((u8, u16), (u32, u16, u8)), 1.2); + offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); //~ ERROR no field `0` +} diff --git a/tests/ui/offset-of/offset-of-builtin.stderr b/tests/ui/offset-of/offset-of-builtin.stderr index 5917ee2936361..c3fee016136c4 100644 --- a/tests/ui/offset-of/offset-of-builtin.stderr +++ b/tests/ui/offset-of/offset-of-builtin.stderr @@ -1,46 +1,117 @@ -error: expected one of `!`, `(`, `+`, `,`, `::`, or `<`, found `)` - --> $DIR/offset-of-builtin.rs:8:43 +error: suffixes on a tuple index are invalid + --> $DIR/offset-of-builtin.rs:16:27 | -LL | builtin # offset_of(NotEnoughArguments); - | ^ expected one of `!`, `(`, `+`, `,`, `::`, or `<` +LL | k#offset_of((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` -error: expected expression, found `)` - --> $DIR/offset-of-builtin.rs:11:55 +error: suffixes on a tuple index are invalid + --> $DIR/offset-of-builtin.rs:10:26 | -LL | builtin # offset_of(NotEnoughArgumentsWithAComma, ); - | ^ expected expression +LL | offset_of!((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` -error: expected `)`, found `too` - --> $DIR/offset-of-builtin.rs:14:31 +error[E0609]: no field `_0` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:7:26 | -LL | builtin # offset_of(S, f, too many arguments); - | ^^^ expected `)` +LL | offset_of!((u8, u8), _0); + | ^^ + | +help: a field with a similar name exists + | +LL - offset_of!((u8, u8), _0); +LL + offset_of!((u8, u8), 0); | - = note: unexpected third argument to offset_of -error: unexpected token: `)` - --> $DIR/offset-of-builtin.rs:20:30 +error[E0609]: no field `01` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:8:26 | -LL | builtin # offset_of(S, f.); - | ^ +LL | offset_of!((u8, u8), 01); + | ^^ + | + = note: available fields are: `0`, `1` -error: unexpected token: `,` - --> $DIR/offset-of-builtin.rs:23:30 +error[E0609]: no field `1e2` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:9:26 + | +LL | offset_of!((u8, u8), 1e2); + | ^^^ | -LL | builtin # offset_of(S, f.,); - | ^ + = note: available fields are: `0`, `1` -error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-builtin.rs:26:28 +error[E0609]: no field `1_` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:10:26 + | +LL | offset_of!((u8, u8), 1_u8); + | ^^^^ + | +help: a field with a similar name exists + | +LL - offset_of!((u8, u8), 1_u8); +LL + offset_of!((u8, u8), 1); + | + +error[E0609]: no field `1e2` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:13:27 + | +LL | k#offset_of((u8, u8), 1e2); + | ^^^ + | + = note: available fields are: `0`, `1` + +error[E0609]: no field `_0` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:14:27 + | +LL | k#offset_of((u8, u8), _0); + | ^^ + | +help: a field with a similar name exists + | +LL - k#offset_of((u8, u8), _0); +LL + k#offset_of((u8, u8), 0); + | + +error[E0609]: no field `01` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:15:27 + | +LL | k#offset_of((u8, u8), 01); + | ^^ + | + = note: available fields are: `0`, `1` + +error[E0609]: no field `1_` on type `(u8, u8)` + --> $DIR/offset-of-builtin.rs:16:27 + | +LL | k#offset_of((u8, u8), 1_u8); + | ^^^^ + | +help: a field with a similar name exists + | +LL - k#offset_of((u8, u8), 1_u8); +LL + k#offset_of((u8, u8), 1); + | + +error[E0609]: no field `2` on type `(u8, u16)` + --> $DIR/offset-of-builtin.rs:19:47 + | +LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); + | ^ + | + = note: available fields are: `0`, `1` + +error[E0609]: no field `1e2` on type `(u8, u16)` + --> $DIR/offset-of-builtin.rs:20:47 + | +LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); + | ^^^ | -LL | builtin # offset_of(S, f..); - | ^^^ + = note: available fields are: `0`, `1` -error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-builtin.rs:29:28 +error[E0609]: no field `0` on type `u8` + --> $DIR/offset-of-builtin.rs:22:49 | -LL | builtin # offset_of(S, f..,); - | ^^^ +LL | offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); + | ^ -error: aborting due to 7 previous errors +error: aborting due to 13 previous errors +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/offset-of/offset-of-tuple-field.rs b/tests/ui/offset-of/offset-of-tuple-field.rs index 02d41f91a2563..dbd5495cf5464 100644 --- a/tests/ui/offset-of/offset-of-tuple-field.rs +++ b/tests/ui/offset-of/offset-of-tuple-field.rs @@ -1,4 +1,5 @@ -#![feature(builtin_syntax)] +//@ edition: 2021.. +#![feature(forced_keywords, builtin_syntax)] use std::mem::offset_of; @@ -9,10 +10,10 @@ fn main() { offset_of!((u8, u8), 1_u8); //~ ERROR no field `1_` //~| ERROR suffixes on a tuple index - builtin # offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` - builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0` - builtin # offset_of((u8, u8), 01); //~ ERROR no field `01` - builtin # offset_of((u8, u8), 1_u8); //~ ERROR no field `1_` + k#offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` + k#offset_of((u8, u8), _0); //~ ERROR no field `_0` + k#offset_of((u8, u8), 01); //~ ERROR no field `01` + k#offset_of((u8, u8), 1_u8); //~ ERROR no field `1_` //~| ERROR suffixes on a tuple index offset_of!(((u8, u16), (u32, u16, u8)), 0.2); //~ ERROR no field `2` diff --git a/tests/ui/offset-of/offset-of-tuple-field.stderr b/tests/ui/offset-of/offset-of-tuple-field.stderr index 01622c5fa2da6..71a0fb222e890 100644 --- a/tests/ui/offset-of/offset-of-tuple-field.stderr +++ b/tests/ui/offset-of/offset-of-tuple-field.stderr @@ -1,17 +1,17 @@ error: suffixes on a tuple index are invalid - --> $DIR/offset-of-tuple-field.rs:15:35 + --> $DIR/offset-of-tuple-field.rs:16:27 | -LL | builtin # offset_of((u8, u8), 1_u8); - | ^^^^ invalid suffix `u8` +LL | k#offset_of((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` error: suffixes on a tuple index are invalid - --> $DIR/offset-of-tuple-field.rs:9:26 + --> $DIR/offset-of-tuple-field.rs:10:26 | LL | offset_of!((u8, u8), 1_u8); | ^^^^ invalid suffix `u8` error[E0609]: no field `_0` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:6:26 + --> $DIR/offset-of-tuple-field.rs:7:26 | LL | offset_of!((u8, u8), _0); | ^^ @@ -23,7 +23,7 @@ LL + offset_of!((u8, u8), 0); | error[E0609]: no field `01` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:7:26 + --> $DIR/offset-of-tuple-field.rs:8:26 | LL | offset_of!((u8, u8), 01); | ^^ @@ -31,7 +31,7 @@ LL | offset_of!((u8, u8), 01); = note: available fields are: `0`, `1` error[E0609]: no field `1e2` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:8:26 + --> $DIR/offset-of-tuple-field.rs:9:26 | LL | offset_of!((u8, u8), 1e2); | ^^^ @@ -39,7 +39,7 @@ LL | offset_of!((u8, u8), 1e2); = note: available fields are: `0`, `1` error[E0609]: no field `1_` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:9:26 + --> $DIR/offset-of-tuple-field.rs:10:26 | LL | offset_of!((u8, u8), 1_u8); | ^^^^ @@ -51,47 +51,47 @@ LL + offset_of!((u8, u8), 1); | error[E0609]: no field `1e2` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:12:35 + --> $DIR/offset-of-tuple-field.rs:13:27 | -LL | builtin # offset_of((u8, u8), 1e2); - | ^^^ +LL | k#offset_of((u8, u8), 1e2); + | ^^^ | = note: available fields are: `0`, `1` error[E0609]: no field `_0` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:13:35 + --> $DIR/offset-of-tuple-field.rs:14:27 | -LL | builtin # offset_of((u8, u8), _0); - | ^^ +LL | k#offset_of((u8, u8), _0); + | ^^ | help: a field with a similar name exists | -LL - builtin # offset_of((u8, u8), _0); -LL + builtin # offset_of((u8, u8), 0); +LL - k#offset_of((u8, u8), _0); +LL + k#offset_of((u8, u8), 0); | error[E0609]: no field `01` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:14:35 + --> $DIR/offset-of-tuple-field.rs:15:27 | -LL | builtin # offset_of((u8, u8), 01); - | ^^ +LL | k#offset_of((u8, u8), 01); + | ^^ | = note: available fields are: `0`, `1` error[E0609]: no field `1_` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:15:35 + --> $DIR/offset-of-tuple-field.rs:16:27 | -LL | builtin # offset_of((u8, u8), 1_u8); - | ^^^^ +LL | k#offset_of((u8, u8), 1_u8); + | ^^^^ | help: a field with a similar name exists | -LL - builtin # offset_of((u8, u8), 1_u8); -LL + builtin # offset_of((u8, u8), 1); +LL - k#offset_of((u8, u8), 1_u8); +LL + k#offset_of((u8, u8), 1); | error[E0609]: no field `2` on type `(u8, u16)` - --> $DIR/offset-of-tuple-field.rs:18:47 + --> $DIR/offset-of-tuple-field.rs:19:47 | LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); | ^ @@ -99,7 +99,7 @@ LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); = note: available fields are: `0`, `1` error[E0609]: no field `1e2` on type `(u8, u16)` - --> $DIR/offset-of-tuple-field.rs:19:47 + --> $DIR/offset-of-tuple-field.rs:20:47 | LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); | ^^^ @@ -107,7 +107,7 @@ LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); = note: available fields are: `0`, `1` error[E0609]: no field `0` on type `u8` - --> $DIR/offset-of-tuple-field.rs:21:49 + --> $DIR/offset-of-tuple-field.rs:22:49 | LL | offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); | ^ diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs index ddbaee97b1bb0..fdb571bc6086f 100644 --- a/tests/ui/offset-of/offset-of-tuple.rs +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -1,4 +1,5 @@ -#![feature(builtin_syntax)] +//@ edition: 2021.. +#![feature(forced_keywords, builtin_syntax)] use std::mem::offset_of; @@ -9,9 +10,9 @@ fn main() { offset_of!((u8, u8), 1 .); //~ ERROR unexpected token: `)` // We need to put these into curly braces, otherwise only one of the // errors will be emitted and the others suppressed. - { builtin # offset_of((u8, u8), +1) }; //~ ERROR leading `+` is not supported - { builtin # offset_of((u8, u8), 1.) }; //~ ERROR offset_of expects dot-separated field and variant names - { builtin # offset_of((u8, u8), 1 .) }; //~ ERROR unexpected token: `)` + { k#offset_of((u8, u8), +1) }; //~ ERROR leading `+` is not supported + { k#offset_of((u8, u8), 1.) }; //~ ERROR offset_of expects dot-separated field and variant names + { k#offset_of((u8, u8), 1 .) }; //~ ERROR unexpected token: `)` } type ComplexTup = (((u8, u8), u8), u8); @@ -26,14 +27,15 @@ fn nested() { offset_of!(ComplexTup, 0.0 . 1.); //~ ERROR unexpected token: `)` offset_of!(ComplexTup, 0.0. 1.); //~ ERROR unexpected token: `)` + // FIXME(fmease): Update comment. // Test for builtin too to ensure that the builtin syntax can also handle these cases // We need to put these into curly braces, otherwise only one of the // errors will be emitted and the others suppressed. - { builtin # offset_of(ComplexTup, 0.0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0 .0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0 . 0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0. 0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0.0 .1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0.0 . 1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0.0. 1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0 .0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0 . 0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0. 0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0 .1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0 . 1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0. 1.) }; //~ ERROR unexpected token: `)` } diff --git a/tests/ui/offset-of/offset-of-tuple.stderr b/tests/ui/offset-of/offset-of-tuple.stderr index f90f2db1c6c35..db46373449218 100644 --- a/tests/ui/offset-of/offset-of-tuple.stderr +++ b/tests/ui/offset-of/offset-of-tuple.stderr @@ -1,71 +1,71 @@ error: leading `+` is not supported - --> $DIR/offset-of-tuple.rs:12:37 + --> $DIR/offset-of-tuple.rs:13:29 | -LL | { builtin # offset_of((u8, u8), +1) }; - | ^ unexpected `+` +LL | { k#offset_of((u8, u8), +1) }; + | ^ unexpected `+` | help: try removing the `+` | -LL - { builtin # offset_of((u8, u8), +1) }; -LL + { builtin # offset_of((u8, u8), 1) }; +LL - { k#offset_of((u8, u8), +1) }; +LL + { k#offset_of((u8, u8), 1) }; | error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-tuple.rs:13:38 + --> $DIR/offset-of-tuple.rs:14:30 | -LL | { builtin # offset_of((u8, u8), 1.) }; - | ^ +LL | { k#offset_of((u8, u8), 1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:14:40 + --> $DIR/offset-of-tuple.rs:15:32 | -LL | { builtin # offset_of((u8, u8), 1 .) }; - | ^ +LL | { k#offset_of((u8, u8), 1 .) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:32:45 + --> $DIR/offset-of-tuple.rs:34:37 | -LL | { builtin # offset_of(ComplexTup, 0.0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:33:46 + --> $DIR/offset-of-tuple.rs:35:38 | -LL | { builtin # offset_of(ComplexTup, 0 .0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0 .0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:34:47 + --> $DIR/offset-of-tuple.rs:36:39 | -LL | { builtin # offset_of(ComplexTup, 0 . 0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0 . 0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:35:46 + --> $DIR/offset-of-tuple.rs:37:38 | -LL | { builtin # offset_of(ComplexTup, 0. 0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0. 0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:36:46 + --> $DIR/offset-of-tuple.rs:38:38 | -LL | { builtin # offset_of(ComplexTup, 0.0 .1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0 .1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:37:47 + --> $DIR/offset-of-tuple.rs:39:39 | -LL | { builtin # offset_of(ComplexTup, 0.0 . 1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0 . 1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:38:46 + --> $DIR/offset-of-tuple.rs:40:38 | -LL | { builtin # offset_of(ComplexTup, 0.0. 1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0. 1.) }; + | ^ error: no rules expected `+` - --> $DIR/offset-of-tuple.rs:6:26 + --> $DIR/offset-of-tuple.rs:7:26 | LL | offset_of!((u8, u8), +1); | ^ no rules expected this token in macro call @@ -75,61 +75,61 @@ note: while trying to match meta-variable `$fields:expr` = note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)` error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-tuple.rs:7:26 + --> $DIR/offset-of-tuple.rs:8:26 | LL | offset_of!((u8, u8), -1); | ^^ error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-tuple.rs:8:27 + --> $DIR/offset-of-tuple.rs:9:27 | LL | offset_of!((u8, u8), 1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:9:29 + --> $DIR/offset-of-tuple.rs:10:29 | LL | offset_of!((u8, u8), 1 .); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:21:34 + --> $DIR/offset-of-tuple.rs:22:34 | LL | offset_of!(ComplexTup, 0.0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:22:35 + --> $DIR/offset-of-tuple.rs:23:35 | LL | offset_of!(ComplexTup, 0 .0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:23:36 + --> $DIR/offset-of-tuple.rs:24:36 | LL | offset_of!(ComplexTup, 0 . 0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:24:35 + --> $DIR/offset-of-tuple.rs:25:35 | LL | offset_of!(ComplexTup, 0. 0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:25:35 + --> $DIR/offset-of-tuple.rs:26:35 | LL | offset_of!(ComplexTup, 0.0 .1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:26:36 + --> $DIR/offset-of-tuple.rs:27:36 | LL | offset_of!(ComplexTup, 0.0 . 1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:27:35 + --> $DIR/offset-of-tuple.rs:28:35 | LL | offset_of!(ComplexTup, 0.0. 1.); | ^ diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index a1bafc2ed9d22..39042096fb22e 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -315,7 +315,7 @@ mod expressions { - const { builtin # offset_of(T, field) }; + const { k#offset_of(T, field) }; } /// ExprKind::MacCall fn expr_mac_call() { "..."; "..."; "..."; } From 9be76ea12d790842d4ae44d52f7493daf8c12de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 13:09:06 +0200 Subject: [PATCH 6/8] Replace `builtin # type_ascribe` with `k#type_ascribe` --- compiler/rustc_ast/src/ast.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 10 ++++- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/macros/mod.rs | 2 +- tests/ui/unpretty/exhaustive.expanded.stdout | 3 +- tests/ui/unpretty/exhaustive.hir.stderr | 40 +++++++++---------- tests/ui/unpretty/exhaustive.hir.stdout | 7 ++-- tests/ui/unpretty/exhaustive.rs | 3 +- 9 files changed, 40 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f32dd7c260478..1d7634ae8c19f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1772,7 +1772,7 @@ pub enum ExprKind { Lit(token::Lit), /// A cast (e.g., `foo as f64`). Cast(Box, Box), - /// A type ascription (e.g., `builtin # type_ascribe(42, usize)`). + /// A type ascription (e.g., `k#type_ascribe(42, usize)`). /// /// Usually not written directly in user code but /// indirectly via the macro `type_ascribe!(...)`. diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index d84024205404c..eb0822ca78d2c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -497,7 +497,7 @@ impl<'a> State<'a> { self.print_type(ty); } ast::ExprKind::Type(expr, ty) => { - self.word("builtin # type_ascribe"); + self.word("k#type_ascribe"); self.popen(); let ib = self.ibox(0); self.print_expr(expr, FixupContext::default()); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e31dedb8dc431..e8d78effc88dc 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1438,6 +1438,9 @@ impl<'a> Parser<'a> { } else if this.token.is_forced_keyword(kw::OffsetOf) { this.bump(); this.parse_expr_offset_of(lo) + } else if this.token.is_forced_keyword(kw::TypeAscribe) { + this.bump(); + this.parse_expr_type_ascribe(lo) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. let at_async = this.check_keyword(exp!(Async)); @@ -1882,7 +1885,6 @@ impl<'a> Parser<'a> { fn parse_expr_builtin(&mut self) -> PResult<'a, Box> { self.parse_builtin(|this, lo, ident| { Ok(match ident.name { - sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), sym::wrap_binder => { Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) } @@ -1962,12 +1964,16 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } - /// Built-in macro for type ascription expressions. pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, Box> { + self.expect(exp!(OpenParen))?; let expr = self.parse_expr()?; self.expect(exp!(Comma))?; let ty = self.parse_ty()?; + // FIXME: Odd not include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly. let span = lo.to(self.token.span); + self.expect(exp!(CloseParen))?; + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index aef3a336b5688..89254cc79df19 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -137,6 +137,7 @@ symbols! { Raw: "raw", Reuse: "reuse", Safe: "safe", + TypeAscribe: "type_ascribe", Union: "union", Yeet: "yeet", // tidy-alphabetical-end @@ -2200,7 +2201,6 @@ symbols! { ty, type_alias_enum_variants, type_alias_impl_trait, - type_ascribe, type_ascription, type_changing_struct_update, type_id, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3e7b404aedbc9..4e090c04346dc 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1915,7 +1915,7 @@ pub(crate) mod builtin { )] #[diagnostic::opaque] pub macro type_ascribe($expr:expr, $ty:ty) { - builtin # type_ascribe($expr, $ty) + k#type_ascribe($expr, $ty) } /// Unstable placeholder for deref patterns. diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 39042096fb22e..2ffbba0f479db 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -16,6 +16,7 @@ #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] +#![feature(forced_keywords)] #![feature(gen_blocks)] #![feature(more_qualified_paths)] #![feature(never_patterns)] @@ -131,7 +132,7 @@ mod expressions { fn expr_cast() { let expr; expr as T; expr as T; } /// ExprKind::Type - fn expr_type() { let expr; builtin # type_ascribe(expr, T); } + fn expr_type() { let expr; k#type_ascribe(expr, T); } /// ExprKind::Let fn expr_let() { diff --git a/tests/ui/unpretty/exhaustive.hir.stderr b/tests/ui/unpretty/exhaustive.hir.stderr index 5ca4b080a95e5..276bc5b5d2573 100644 --- a/tests/ui/unpretty/exhaustive.hir.stderr +++ b/tests/ui/unpretty/exhaustive.hir.stderr @@ -1,17 +1,17 @@ error[E0697]: closures cannot be static - --> $DIR/exhaustive.rs:209:9 + --> $DIR/exhaustive.rs:210:9 | LL | static || value; | ^^^^^^^^^ error[E0697]: closures cannot be static - --> $DIR/exhaustive.rs:210:9 + --> $DIR/exhaustive.rs:211:9 | LL | static move || value; | ^^^^^^^^^^^^^^ error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/exhaustive.rs:239:13 + --> $DIR/exhaustive.rs:240:13 | LL | fn expr_await() { | --------------- this is not `async` @@ -20,19 +20,19 @@ LL | fut.await; | ^^^^^ only allowed inside `async` functions and blocks error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/exhaustive.rs:290:9 + --> $DIR/exhaustive.rs:291:9 | LL | _; | ^ `_` not allowed here error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:300:9 + --> $DIR/exhaustive.rs:301:9 | LL | x::(); | ^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:301:9 + --> $DIR/exhaustive.rs:302:9 | LL | x::(T, T) -> T; | ^^^^^^^^^^^^^^ only `Fn` traits may use parentheses @@ -44,31 +44,31 @@ LL + x:: -> T; | error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:302:9 + --> $DIR/exhaustive.rs:303:9 | LL | crate::() -> ()::expressions::() -> ()::expr_path; | ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:302:26 + --> $DIR/exhaustive.rs:303:26 | LL | crate::() -> ()::expressions::() -> ()::expr_path; | ^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:305:9 + --> $DIR/exhaustive.rs:306:9 | LL | core::()::marker::()::PhantomData; | ^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:305:19 + --> $DIR/exhaustive.rs:306:19 | LL | core::()::marker::()::PhantomData; | ^^^^^^^^^^ only `Fn` traits may use parentheses error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks - --> $DIR/exhaustive.rs:392:9 + --> $DIR/exhaustive.rs:393:9 | LL | yield; | ^^^^^ @@ -79,7 +79,7 @@ LL | #[coroutine] fn expr_yield() { | ++++++++++++ error[E0703]: invalid ABI: found `C++` - --> $DIR/exhaustive.rs:472:23 + --> $DIR/exhaustive.rs:473:23 | LL | unsafe extern "C++" {} | ^^^^^ invalid ABI @@ -87,7 +87,7 @@ LL | unsafe extern "C++" {} = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions error: `..` patterns are not allowed here - --> $DIR/exhaustive.rs:674:13 + --> $DIR/exhaustive.rs:675:13 | LL | let ..; | ^^ @@ -95,13 +95,13 @@ LL | let ..; = note: only allowed in tuple, tuple struct, and slice patterns error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:789:16 + --> $DIR/exhaustive.rs:790:16 | LL | let _: T() -> !; | ^^^^^^^^ only `Fn` traits may use parentheses error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:803:16 + --> $DIR/exhaustive.rs:804:16 | LL | let _: impl Send; | ^^^^^^^^^ @@ -112,7 +112,7 @@ LL | let _: impl Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:804:16 + --> $DIR/exhaustive.rs:805:16 | LL | let _: impl Send + 'static; | ^^^^^^^^^^^^^^^^^^^ @@ -123,7 +123,7 @@ LL | let _: impl Send + 'static; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:805:16 + --> $DIR/exhaustive.rs:806:16 | LL | let _: impl 'static + Send; | ^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | let _: impl 'static + Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:806:16 + --> $DIR/exhaustive.rs:807:16 | LL | let _: impl ?Sized; | ^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | let _: impl ?Sized; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:807:16 + --> $DIR/exhaustive.rs:808:16 | LL | let _: impl [const] Clone; | ^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let _: impl [const] Clone; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:808:16 + --> $DIR/exhaustive.rs:809:16 | LL | let _: impl for<'a> Send; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 1b18e00c951c8..cb8d4ec0beda9 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -11,9 +11,10 @@ #![allow(incomplete_features)] #![attr = Feature([auto_traits#0, builtin_syntax#0, const_trait_impl#0, coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, -gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, -pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, -trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] +forced_keywords#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, +pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, +trace_macros#0, trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, +yeet_expr#0])] extern crate std; #[attr = PreludeImport] use std::prelude::rust_2024::*; diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 62a8b6b9ecc20..46139e5a2a4ce 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -15,6 +15,7 @@ #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] +#![feature(forced_keywords)] #![feature(gen_blocks)] #![feature(more_qualified_paths)] #![feature(never_patterns)] @@ -144,7 +145,7 @@ mod expressions { /// ExprKind::Type fn expr_type() { let expr; - builtin # type_ascribe(expr, T); + k#type_ascribe(expr, T); } /// ExprKind::Let From 0326abb4fe9deaf5f08a6e8683b443c87d218875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 13:23:59 +0200 Subject: [PATCH 7/8] Replace `builtin # {,un}wrap_binder` with `k#{,un}wrap_binder` --- .../rustc_ast_pretty/src/pprust/state/expr.rs | 5 +- compiler/rustc_parse/src/diagnostics.rs | 15 ----- compiler/rustc_parse/src/parser/expr.rs | 66 ++++--------------- compiler/rustc_parse/src/parser/item.rs | 8 --- compiler/rustc_parse/src/parser/stmt.rs | 6 +- compiler/rustc_span/src/symbol.rs | 5 +- library/core/src/unsafe_binder.rs | 18 ++--- tests/ui/parser/builtin-syntax.rs | 9 --- tests/ui/parser/builtin-syntax.stderr | 14 ---- 9 files changed, 21 insertions(+), 125 deletions(-) delete mode 100644 tests/ui/parser/builtin-syntax.rs delete mode 100644 tests/ui/parser/builtin-syntax.stderr diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index eb0822ca78d2c..a8d4c1b32e107 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -855,10 +855,9 @@ impl<'a> State<'a> { self.print_block_with_attrs(blk, attrs, cb, ib) } ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => { - self.word("builtin # "); match kind { - ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder"), - ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"), + ast::UnsafeBinderCastKind::Wrap => self.word("k#wrap_binder"), + ast::UnsafeBinderCastKind::Unwrap => self.word("k#unwrap_binder"), } self.popen(); let ib = self.ibox(0); diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 4d6f37903ec60..fb6e1d5e8991d 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -4077,21 +4077,6 @@ impl IntoDiagArg for Case { } } -#[derive(Diagnostic)] -#[diag("unknown `builtin #` construct `{$name}`")] -pub(crate) struct UnknownBuiltinConstruct { - #[primary_span] - pub span: Span, - pub name: Ident, -} - -#[derive(Diagnostic)] -#[diag("expected identifier after `builtin #`")] -pub(crate) struct ExpectedBuiltinIdent { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("static items may not have generic parameters")] pub(crate) struct StaticWithGenerics { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e8d78effc88dc..6c03074514352 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1369,8 +1369,6 @@ impl<'a> Parser<'a> { }) } else if this.check(exp!(OpenBracket)) { this.parse_expr_array_or_repeat(exp!(CloseBracket)) - } else if this.is_builtin() { - this.parse_expr_builtin() } else if this.check_path() { this.parse_expr_path_start() } else if this.check_keyword(exp!(Move)) @@ -1441,6 +1439,12 @@ impl<'a> Parser<'a> { } else if this.token.is_forced_keyword(kw::TypeAscribe) { this.bump(); this.parse_expr_type_ascribe(lo) + } else if this.token.is_forced_keyword(kw::WrapBinder) { + this.bump(); + this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap) + } else if this.token.is_forced_keyword(kw::UnwrapBinder) { + this.bump(); + this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. let at_async = this.check_keyword(exp!(Async)); @@ -1881,54 +1885,6 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } - /// Parse `builtin # ident(args,*)`. - fn parse_expr_builtin(&mut self) -> PResult<'a, Box> { - self.parse_builtin(|this, lo, ident| { - Ok(match ident.name { - sym::wrap_binder => { - Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) - } - sym::unwrap_binder => { - Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?) - } - _ => None, - }) - }) - } - - pub(crate) fn parse_builtin( - &mut self, - parse: impl FnOnce(&mut Parser<'a>, Span, Ident) -> PResult<'a, Option>, - ) -> PResult<'a, T> { - let lo = self.token.span; - - self.bump(); // `builtin` - self.bump(); // `#` - - let Some((ident, IdentKind::Normal)) = self.token.ident() else { - let err = self - .dcx() - .create_err(crate::diagnostics::ExpectedBuiltinIdent { span: self.token.span }); - return Err(err); - }; - self.psess.gated_spans.gate(sym::builtin_syntax, ident.span); - self.bump(); - - self.expect(exp!(OpenParen))?; - let ret = if let Some(res) = parse(self, lo, ident)? { - Ok(res) - } else { - let err = self.dcx().create_err(crate::diagnostics::UnknownBuiltinConstruct { - span: lo.to(ident.span), - name: ident, - }); - return Err(err); - }; - self.expect(exp!(CloseParen))?; - - ret - } - pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, Box> { self.expect(exp!(OpenParen))?; @@ -1982,9 +1938,15 @@ impl<'a> Parser<'a> { lo: Span, kind: UnsafeBinderCastKind, ) -> PResult<'a, Box> { + self.expect(exp!(OpenParen))?; + let expr = self.parse_expr()?; let ty = if self.eat(exp!(Comma)) { Some(self.parse_ty()?) } else { None }; + // FIXME: Odd not include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly. let span = lo.to(self.token.span); + self.expect(exp!(CloseParen))?; + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) } @@ -3510,10 +3472,6 @@ impl<'a> Parser<'a> { Ok(expr) } - pub(crate) fn is_builtin(&self) -> bool { - self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound) - } - /// Parses a `try {...}` or `try bikeshed Ty {...}` expression (`try` token already eaten). fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, Box> { let annotation = diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e202d7833a73a..9acb1f0ee4a83 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -350,9 +350,6 @@ impl<'a> Parser<'a> { // UNION ITEM self.bump(); // `union` self.parse_item_union()? - } else if self.is_builtin() { - // BUILTIN# ITEM - return self.parse_item_builtin(); } else if self.eat_keyword_case(exp!(Macro), case) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? @@ -558,11 +555,6 @@ impl<'a> Parser<'a> { if let Some(err) = err { Err(self.dcx().create_err(err)) } else { Ok(()) } } - fn parse_item_builtin(&mut self) -> PResult<'a, Option> { - // To be expanded - Ok(None) - } - /// Parses an item macro, e.g., `item!();`. fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> { let path = self.parse_path(PathStyle::Mod)?; // `foo::bar` diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index a082ea678737d..85fc5920f0c44 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -127,11 +127,7 @@ impl<'a> Parser<'a> { diagnostics::InvalidVariableDeclarationSub::UseLetNotVar, force_collect, )? - } else if self.check_path() - && !self.token.is_qpath_start() - && !self.is_path_start_item() - && !self.is_builtin() - { + } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { // We have avoided contextual keywords like `union`, items with `crate` visibility, // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something // that starts like a path (1 token), but it fact not a path. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 89254cc79df19..e0dfab541d696 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -124,7 +124,6 @@ symbols! { // Matching predicates: `is_weak` // tidy-alphabetical-start Auto: "auto", - Builtin: "builtin", Catch: "catch", ContractEnsures: "contract_ensures", ContractRequires: "contract_requires", @@ -139,6 +138,8 @@ symbols! { Safe: "safe", TypeAscribe: "type_ascribe", Union: "union", + UnwrapBinder: "unwrap_binder", + WrapBinder: "wrap_binder", Yeet: "yeet", // tidy-alphabetical-end } @@ -2324,7 +2325,6 @@ symbols! { unwind_attributes, unwind_safe_trait, unwrap, - unwrap_binder, unwrap_or, update, use_cloned, @@ -2412,7 +2412,6 @@ symbols! { windows, windows_subsystem, with_negative_coherence, - wrap_binder, wrapping_add, wrapping_div, wrapping_mul, diff --git a/library/core/src/unsafe_binder.rs b/library/core/src/unsafe_binder.rs index 3d2a04e95f078..923ee76c11b44 100644 --- a/library/core/src/unsafe_binder.rs +++ b/library/core/src/unsafe_binder.rs @@ -4,24 +4,14 @@ #[allow_internal_unstable(builtin_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] -pub macro unwrap_binder { - ($expr:expr) => { - builtin # unwrap_binder ( $expr ) - }, - ($expr:expr ; $ty:ty) => { - builtin # unwrap_binder ( $expr, $ty ) - }, +pub macro unwrap_binder($expr:expr $( ; $ty:ty )?) { + k#unwrap_binder($expr $( , $ty )?) } /// Wrap a type into an unsafe binder. #[allow_internal_unstable(builtin_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] -pub macro wrap_binder { - ($expr:expr) => { - builtin # wrap_binder ( $expr ) - }, - ($expr:expr ; $ty:ty) => { - builtin # wrap_binder ( $expr, $ty ) - }, +pub macro wrap_binder($expr:expr $( ; $ty:ty )?) { + k#wrap_binder($expr $( , $ty )?) } diff --git a/tests/ui/parser/builtin-syntax.rs b/tests/ui/parser/builtin-syntax.rs deleted file mode 100644 index 897dab8ec50ae..0000000000000 --- a/tests/ui/parser/builtin-syntax.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(builtin_syntax)] - -fn main() { - builtin # foobar(); //~ ERROR unknown `builtin #` construct -} - -fn not_identifier() { - builtin # {}(); //~ ERROR expected identifier after -} diff --git a/tests/ui/parser/builtin-syntax.stderr b/tests/ui/parser/builtin-syntax.stderr deleted file mode 100644 index ee3764a62216a..0000000000000 --- a/tests/ui/parser/builtin-syntax.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: unknown `builtin #` construct `foobar` - --> $DIR/builtin-syntax.rs:4:5 - | -LL | builtin # foobar(); - | ^^^^^^^^^^^^^^^^ - -error: expected identifier after `builtin #` - --> $DIR/builtin-syntax.rs:8:15 - | -LL | builtin # {}(); - | ^ - -error: aborting due to 2 previous errors - From 25f51e1015ff4a8dac6b3b7afb76d47be4af882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 14:12:44 +0200 Subject: [PATCH 8/8] Rename feature `builtin_syntax` to `internal_syntax` --- compiler/rustc_ast/src/token.rs | 4 ++-- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 4 ++-- compiler/rustc_ast_pretty/src/pprust/state/item.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 4 ++-- compiler/rustc_parse/src/parser/expr.rs | 6 +++--- compiler/rustc_parse/src/parser/pat.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/field.rs | 2 +- library/core/src/macros/mod.rs | 4 ++-- library/core/src/mem/mod.rs | 2 +- library/core/src/unsafe_binder.rs | 4 ++-- ...te-builtin_syntax.rs => feature-gate-internal_syntax.rs} | 2 +- ...in_syntax.stderr => feature-gate-internal_syntax.stderr} | 6 +++--- tests/ui/offset-of/offset-of-builtin.rs | 2 +- tests/ui/offset-of/offset-of-tuple-field.rs | 2 +- tests/ui/offset-of/offset-of-tuple.rs | 2 +- tests/ui/unpretty/exhaustive.expanded.stdout | 2 +- tests/ui/unpretty/exhaustive.hir.stdout | 2 +- tests/ui/unpretty/exhaustive.rs | 2 +- 21 files changed, 30 insertions(+), 30 deletions(-) rename tests/ui/feature-gates/{feature-gate-builtin_syntax.rs => feature-gate-internal_syntax.rs} (60%) rename tests/ui/feature-gates/{feature-gate-builtin_syntax.stderr => feature-gate-internal_syntax.stderr} (68%) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 95f3d91f2df47..08f3987b6466b 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -313,7 +313,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `expr` nonterminal which is user observable. - // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + // NOTE: We don't care about forced keywords that are gated behind `internal_syntax`. let ident_token = Token::new(Ident(name, kind), span); @@ -352,7 +352,7 @@ fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `ty` nonterminal which is user observable. - // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + // NOTE: We don't care about forced keywords that are gated behind `internal_syntax`. let ident_token = Token::new(Ident(name, kind), span); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index e1871bc4897cd..a31667136f984 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -429,7 +429,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // tidy-alphabetical-start gate_all!(async_for_loop, "`for await` loops are experimental"); - gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(const_block_items, "const block items are experimental"); gate_all!(const_closures, "const closures are experimental"); gate_all!(const_trait_impl, "const trait impls are experimental"); @@ -448,6 +447,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(global_registration, "global registration is experimental"); gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(impl_restriction, "`impl` restrictions are experimental"); + gate_all!(internal_syntax, "this syntax is internal"); gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental"); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(move_expr, "`move(expr)` syntax is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index a8d4c1b32e107..88fce7523c3b8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -777,12 +777,12 @@ impl<'a> State<'a> { self.print_expr(result, fixup.rightmost_subexpression()); } ast::ExprKind::InlineAsm(a) => { - // FIXME: Print `builtin # asm` once macro `asm` uses `builtin_syntax`. + // FIXME: Print `k#asm` once macro `asm` uses `internal_syntax`. self.word(format!("{}!", a.asm_macro.macro_name())); self.print_inline_asm(a); } ast::ExprKind::FormatArgs(fmt) => { - // FIXME: Print `builtin # format_args` once macro `format_args` uses `builtin_syntax`. + // FIXME: Print `k#format_args` once macro `format_args` uses `internal_syntax`. self.word("format_args!"); self.popen(); let ib = self.ibox(0); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 6ce34cecc7e97..84d620b9b5250 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -288,7 +288,7 @@ impl<'a> State<'a> { self.bclose(item.span, empty, cb); } ast::ItemKind::GlobalAsm(asm) => { - // FIXME: Print `builtin # global_asm` once macro `global_asm` uses `builtin_syntax`. + // FIXME: Print `k#global_asm` once macro `global_asm` uses `internal_syntax`. let (cb, ib) = self.head(visibility_qualified(&item.vis, "global_asm!")); self.print_inline_asm(asm); self.word(";"); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 2b8a2629457ca..7465ffffc1906 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -292,8 +292,6 @@ declare_features! ( /// Allows features specific to auto traits. /// Renamed from `optin_builtin_traits`. (unstable, auto_traits, "1.50.0", Some(13231)), - /// Allows builtin # foo() syntax - (internal, builtin_syntax, "1.71.0", Some(110680)), /// Allows `#[doc(notable_trait)]`. /// Renamed from `doc_spotlight`. (unstable, doc_notable_trait, "1.52.0", Some(45040)), @@ -301,6 +299,8 @@ declare_features! ( (unstable, dropck_eyepatch, "1.10.0", Some(34761)), /// Allows using the `#[fundamental]` attribute. (unstable, fundamental, "1.0.0", Some(29635)), + /// Allows internal syntax. + (internal, internal_syntax, "CURRENT_RUSTC_VERSION", Some(110680)), /// Allows using `#[link_name="llvm.*"]`. (internal, link_llvm_intrinsics, "1.0.0", Some(29602)), /// Allows using the `#[linkage = ".."]` attribute. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6c03074514352..584585e43ed7b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1915,7 +1915,7 @@ impl<'a> Parser<'a> { let span = lo.to(self.token.span); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } @@ -1929,7 +1929,7 @@ impl<'a> Parser<'a> { // it actually "improves" diagnostics slightly. let span = lo.to(self.token.span); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) } @@ -1946,7 +1946,7 @@ impl<'a> Parser<'a> { // it actually "improves" diagnostics slightly. let span = lo.to(self.token.span); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c8c4182486422..68c2a4c4988a5 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -811,7 +811,7 @@ impl<'a> Parser<'a> { CommaRecoveryMode::LikelyTuple, )?)); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); pat } // Don't eagerly error on semantically invalid tokens when matching diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 43d3cc7c40e3b..f19873eaa8a71 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -826,7 +826,7 @@ impl<'a> Parser<'a> { let span = self.token.span; self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); match *fields { [] => Err(self diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e0dfab541d696..be30263cfd782 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -580,7 +580,6 @@ symbols! { bridge, bswap, built, - builtin_syntax, bundle, c_dash_variadic, c_str_literals, @@ -1171,6 +1170,7 @@ symbols! { internal, internal_eq_trait_method_impls, internal_features, + internal_syntax, interrupt, into_async_iter_into_iter, into_future, diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 07c8c991bac43..b17a78846ce2a 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -124,7 +124,7 @@ impl Ord /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the /// variant must also be specified. Only a single field is supported. #[unstable(feature = "field_projections", issue = "145383")] -#[allow_internal_unstable(field_representing_type_raw, builtin_syntax)] +#[allow_internal_unstable(field_representing_type_raw, internal_syntax)] #[diagnostic::on_unmatched_args( note = "this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`" )] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 4e090c04346dc..ffe6dfb4106da 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1907,7 +1907,7 @@ pub(crate) mod builtin { } /// Unstable placeholder for type ascription. - #[allow_internal_unstable(builtin_syntax)] + #[allow_internal_unstable(internal_syntax)] #[unstable( feature = "type_ascription", issue = "23416", @@ -1919,7 +1919,7 @@ pub(crate) mod builtin { } /// Unstable placeholder for deref patterns. - #[allow_internal_unstable(builtin_syntax)] + #[allow_internal_unstable(internal_syntax)] #[unstable( feature = "deref_patterns", issue = "87121", diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 13c61887827e0..54a132f40f3e5 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1664,7 +1664,7 @@ impl SizedTypeProperties for T {} note = "this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`" )] #[doc(alias = "memoffset")] -#[allow_internal_unstable(core_intrinsics, builtin_syntax)] +#[allow_internal_unstable(core_intrinsics, internal_syntax)] #[diagnostic::opaque] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { const { k#offset_of($Container, $($fields)+) } diff --git a/library/core/src/unsafe_binder.rs b/library/core/src/unsafe_binder.rs index 923ee76c11b44..bb7144fc5dddd 100644 --- a/library/core/src/unsafe_binder.rs +++ b/library/core/src/unsafe_binder.rs @@ -1,7 +1,7 @@ //! Operators used to turn types into unsafe binders and back. /// Unwrap an unsafe binder into its underlying type. -#[allow_internal_unstable(builtin_syntax)] +#[allow_internal_unstable(internal_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] pub macro unwrap_binder($expr:expr $( ; $ty:ty )?) { @@ -9,7 +9,7 @@ pub macro unwrap_binder($expr:expr $( ; $ty:ty )?) { } /// Wrap a type into an unsafe binder. -#[allow_internal_unstable(builtin_syntax)] +#[allow_internal_unstable(internal_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] pub macro wrap_binder($expr:expr $( ; $ty:ty )?) { diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs b/tests/ui/feature-gates/feature-gate-internal_syntax.rs similarity index 60% rename from tests/ui/feature-gates/feature-gate-builtin_syntax.rs rename to tests/ui/feature-gates/feature-gate-internal_syntax.rs index e36ffe1503956..5b4293d900923 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs +++ b/tests/ui/feature-gates/feature-gate-internal_syntax.rs @@ -6,5 +6,5 @@ struct Foo { w: u8, } fn main() { - k#offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable + k#offset_of(Foo, v); //~ ERROR this syntax is internal } diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr b/tests/ui/feature-gates/feature-gate-internal_syntax.stderr similarity index 68% rename from tests/ui/feature-gates/feature-gate-builtin_syntax.stderr rename to tests/ui/feature-gates/feature-gate-internal_syntax.stderr index 59a39df794c44..9db1a51e0d999 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr +++ b/tests/ui/feature-gates/feature-gate-internal_syntax.stderr @@ -1,11 +1,11 @@ -error[E0658]: `builtin #` syntax is unstable - --> $DIR/feature-gate-builtin_syntax.rs:9:5 +error[E0658]: this syntax is internal + --> $DIR/feature-gate-internal_syntax.rs:9:5 | LL | k#offset_of(Foo, v); | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #110680 for more information - = help: add `#![feature(builtin_syntax)]` to the crate attributes to enable + = help: add `#![feature(internal_syntax)]` 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: aborting due to 1 previous error diff --git a/tests/ui/offset-of/offset-of-builtin.rs b/tests/ui/offset-of/offset-of-builtin.rs index dbd5495cf5464..1cb2d31a03e40 100644 --- a/tests/ui/offset-of/offset-of-builtin.rs +++ b/tests/ui/offset-of/offset-of-builtin.rs @@ -1,5 +1,5 @@ //@ edition: 2021.. -#![feature(forced_keywords, builtin_syntax)] +#![feature(forced_keywords, internal_syntax)] use std::mem::offset_of; diff --git a/tests/ui/offset-of/offset-of-tuple-field.rs b/tests/ui/offset-of/offset-of-tuple-field.rs index dbd5495cf5464..1cb2d31a03e40 100644 --- a/tests/ui/offset-of/offset-of-tuple-field.rs +++ b/tests/ui/offset-of/offset-of-tuple-field.rs @@ -1,5 +1,5 @@ //@ edition: 2021.. -#![feature(forced_keywords, builtin_syntax)] +#![feature(forced_keywords, internal_syntax)] use std::mem::offset_of; diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs index fdb571bc6086f..be668d9e08946 100644 --- a/tests/ui/offset-of/offset-of-tuple.rs +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -1,5 +1,5 @@ //@ edition: 2021.. -#![feature(forced_keywords, builtin_syntax)] +#![feature(forced_keywords, internal_syntax)] use std::mem::offset_of; diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 2ffbba0f479db..879a28cafa7b2 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -10,7 +10,7 @@ // errors that only occur once we get past the AST. #![feature(auto_traits)] -#![feature(builtin_syntax)] +#![feature(internal_syntax)] #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index cb8d4ec0beda9..176758a88e450 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -9,7 +9,7 @@ // errors that only occur once we get past the AST. #![allow(incomplete_features)] -#![attr = Feature([auto_traits#0, builtin_syntax#0, const_trait_impl#0, +#![attr = Feature([auto_traits#0, internal_syntax#0, const_trait_impl#0, coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, forced_keywords#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 46139e5a2a4ce..795ca8ff894b9 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -9,7 +9,7 @@ // errors that only occur once we get past the AST. #![feature(auto_traits)] -#![feature(builtin_syntax)] +#![feature(internal_syntax)] #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)]