Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl Lit {
/// `Parser::eat_token_lit` (excluding unary negation).
pub fn from_token(token: &Token) -> Option<Lit> {
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),
Expand Down Expand Up @@ -362,19 +362,23 @@ fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool {
pub enum IdentKind {
Normal,
Raw,
ForcedKeyword,
}

impl IdentKind {
pub fn to_print_mode_ident(self) -> IdentPrintMode {
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!(),
}
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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, .. } => {
Expand Down Expand Up @@ -962,23 +969,21 @@ 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 {
self.non_reserved_ident().is_some()
}

pub fn non_reserved_ident(&self) -> Option<sp::Ident> {
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<sp::Ident> {
match self.ident() {
Some((id, IdentKind::Normal)) => Some(id),
Some((id, IdentKind::Normal | IdentKind::ForcedKeyword)) => Some(id),
_ => None,
}
}
Expand Down Expand Up @@ -1102,6 +1107,14 @@ impl PartialEq<TokenKind> 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,
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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(gca_min_const_items, "unbraced const blocks as const args are experimental");
gate_all!(gen_blocks, "gen blocks are experimental");
Expand Down
10 changes: 4 additions & 6 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_expand/src/mbe/macro_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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),
Expand Down
22 changes: 18 additions & 4 deletions compiler/rustc_expand/src/mbe/metavar_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -197,6 +196,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()
Expand Down Expand Up @@ -281,9 +284,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);
Expand Down Expand Up @@ -347,3 +348,16 @@ 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, ()> {
let kind = match kind {
IdentKind::Normal => return Ok(()),
IdentKind::Raw => "raw identifiers",
IdentKind::ForcedKeyword => "forced keywords",
};
Err(dcx.struct_span_err(span, format!("`${{concat(..)}}` currently does not support {kind}")))
}
9 changes: 6 additions & 3 deletions compiler/rustc_expand/src/mbe/quoted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,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)
Expand Down
16 changes: 5 additions & 11 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -1033,21 +1033,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 {
Expand Down
52 changes: 36 additions & 16 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -103,6 +104,26 @@ impl ToInternal<tk::LitKind> for LitKind {
}
}

Comment thread
petrochenkov marked this conversation as resolved.
Resolved
impl FromInternal<tk::IdentKind> 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<tk::IdentKind> 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<TokenStream> for Vec<TokenTree<TokenStream, Span, Symbol>> {
fn from_internal(stream: TokenStream) -> Self {
// Estimate the capacity as `stream.len()` rounded up to the next power
Expand All @@ -127,7 +148,7 @@ impl FromInternal<TokenStream> for Vec<TokenTree<TokenStream, Span, Symbol>> {
}

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,
Expand Down Expand Up @@ -230,12 +251,12 @@ impl FromInternal<TokenStream> for Vec<TokenTree<TokenStream, Span, Symbol>> {

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,
})),

Expand All @@ -245,7 +266,7 @@ impl FromInternal<TokenStream> for Vec<TokenTree<TokenStream, Span, Symbol>> {
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,
}),
]);
Expand All @@ -254,7 +275,7 @@ impl FromInternal<TokenStream> for Vec<TokenTree<TokenStream, Span, Symbol>> {
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),
}))
Expand Down Expand Up @@ -286,7 +307,7 @@ impl FromInternal<TokenStream> for Vec<TokenTree<TokenStream, Span, Symbol>> {
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),
}));
Expand Down Expand Up @@ -364,17 +385,16 @@ impl ToInternal<SmallVec<[tokenstream::TokenTree; 2]>>
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);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),

@fmease fmease Aug 25, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've intentionally not marked it internal even though this feature is only backed by a T-compiler MCP and not by a T-lang RFC or in-tree experiment:

I would find it a bit weird if we (in a hypothetical future) told users to "please try out" "k#only bounds" or "&k#own types & exprs" (features that would obviously be non-internal) but they'd have to enable an internal feature in tandem (could be perceived as off-putting).

However, I'm okay with switching it to incomplete or even internal if requested.

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No opinion on this.

/// Allows impls for the Freeze trait.
(internal, freeze_impls, "1.78.0", Some(121675)),
/// Frontmatter `---` blocks for use by external tools.
Expand Down
Loading
Loading