Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] implement explicit deref patterns through k#deref #119467

Closed
wants to merge 9 commits into from
Closed
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
7 changes: 6 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,9 @@ impl Pat {
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),

// Trivial wrappers over inner patterns.
PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) | PatKind::Deref(s) => {
s.walk(it)
}

// These patterns do not contain subpatterns, skip.
PatKind::Wild
Expand Down Expand Up @@ -778,6 +780,9 @@ pub enum PatKind {
/// A reference pattern (e.g., `&mut (a, b)`).
Ref(P<Pat>, Mutability),

/// A deref pattern, `k#deref a`.
Deref(P<Pat>),

/// A literal.
Lit(P<Expr>),

Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,8 +1271,6 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
vis.visit_path(path);
fields.flat_map_in_place(|field| vis.flat_map_pat_field(field));
}
PatKind::Box(inner) => vis.visit_pat(inner),
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
visit_opt(e1, |e| vis.visit_expr(e));
visit_opt(e2, |e| vis.visit_expr(e));
Expand All @@ -1281,7 +1279,10 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
visit_thin_vec(elems, |elem| vis.visit_pat(elem))
}
PatKind::Paren(inner) => vis.visit_pat(inner),
PatKind::Paren(inner)
| PatKind::Box(inner)
| PatKind::Ref(inner, _)
| PatKind::Deref(inner) => vis.visit_pat(inner),
PatKind::MacCall(mac) => vis.visit_mac_call(mac),
}
vis.visit_span(span);
Expand Down
82 changes: 60 additions & 22 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ impl Lit {
/// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation.
pub fn from_token(token: &Token) -> Option<Lit> {
match token.uninterpolate().kind {
Ident(name, false) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
Ident(name, IdentKind::Default) if name.is_bool_lit() => {
Some(Lit::new(Bool, name, None))
}
Literal(token_lit) => Some(token_lit),
Interpolated(ref nt)
if let NtExpr(expr) | NtLiteral(expr) = &nt.0
Expand Down Expand Up @@ -183,8 +185,8 @@ impl LitKind {
}
}

pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
let ident_token = Token::new(Ident(name, is_raw), span);
pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool {
let ident_token = Token::new(Ident(name, kind), span);

!ident_token.is_reserved_ident()
|| ident_token.is_path_segment_keyword()
Expand Down Expand Up @@ -212,15 +214,37 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
kw::Static,
]
.contains(&name)
|| kind == IdentKind::Keyword
}

fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool {
let ident_token = Token::new(Ident(name, is_raw), span);
fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool {
let ident_token = Token::new(Ident(name, kind), span);

!ident_token.is_reserved_ident()
|| ident_token.is_path_segment_keyword()
|| [kw::Underscore, kw::For, kw::Impl, kw::Fn, kw::Unsafe, kw::Extern, kw::Typeof, kw::Dyn]
.contains(&name)
|| kind == IdentKind::Keyword
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable_Generic, Encodable, Decodable)]
pub enum IdentKind {
/// The usual identifiers (or, depending on the context, keywords): `v`, `union`, `await`, `loop`.
Default,
/// Raw identifiers: `r#just_an_ident`, `r#loop`.
Raw,
/// Forced keywords: `k#break`, `k#await`, `k#some_new_experimental_keyword`.
Keyword,
}

impl IdentKind {
pub fn prefix(self) -> Option<&'static str> {
match self {
IdentKind::Default => None,
IdentKind::Raw => Some("r#"),
IdentKind::Keyword => Some("k#"),
}
}
}

// SAFETY: due to the `Clone` impl below, all fields of all variants other than
Expand Down Expand Up @@ -298,7 +322,8 @@ pub enum TokenKind {
/// Do not forget about `NtIdent` when you want to match on identifiers.
/// It's recommended to use `Token::(ident,uninterpolate,uninterpolated_span)` to
/// treat regular and interpolated identifiers in the same way.
Ident(Symbol, /* is_raw */ bool),
Ident(Symbol, IdentKind),

/// Lifetime identifier token.
/// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
/// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
Expand Down Expand Up @@ -411,7 +436,13 @@ impl Token {

/// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary.
pub fn from_ast_ident(ident: Ident) -> Self {
Token::new(Ident(ident.name, ident.is_raw_guess()), ident.span)
Token::new(
Ident(
ident.name,
if ident.is_raw_guess() { IdentKind::Raw } else { IdentKind::Default },
),
ident.span,
)
}

/// For interpolated tokens, returns a span of the fragment to which the interpolated
Expand Down Expand Up @@ -567,7 +598,7 @@ impl Token {
pub fn can_begin_literal_maybe_minus(&self) -> bool {
match self.uninterpolate().kind {
Literal(..) | BinOp(Minus) => true,
Ident(name, false) if name.is_bool_lit() => true,
Ident(name, IdentKind::Default) if name.is_bool_lit() => true,
Interpolated(ref nt) => match &nt.0 {
NtLiteral(_) => true,
NtExpr(e) => match &e.kind {
Expand Down Expand Up @@ -602,10 +633,10 @@ impl Token {

/// Returns an identifier if this token is an identifier.
#[inline]
pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> {
pub fn ident(&self) -> Option<(Ident, IdentKind)> {
// We avoid using `Token::uninterpolate` here because it's slow.
match &self.kind {
&Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
&Ident(name, kind) => Some((Ident::new(name, self.span), kind)),
Interpolated(nt) => match &nt.0 {
NtIdent(ident, is_raw) => Some((*ident, *is_raw)),
_ => None,
Expand Down Expand Up @@ -698,46 +729,53 @@ impl Token {

/// Returns `true` if the token is a given keyword, `kw`.
pub fn is_keyword(&self, kw: Symbol) -> bool {
self.is_non_raw_ident_where(|id| id.name == kw)
self.is_keywordable_ident_where(|id| id.name == kw)
}

pub fn is_forced_keyword(&self, kw: Symbol) -> bool {
match self.ident() {
Some((id, IdentKind::Keyword)) => id.name == kw,
_ => false,
}
}

/// 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 {
self.is_keyword(kw)
|| (case == Case::Insensitive
&& self.is_non_raw_ident_where(|id| {
&& self.is_keywordable_ident_where(|id| {
id.name.as_str().to_lowercase() == kw.as_str().to_lowercase()
}))
}

pub fn is_path_segment_keyword(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
self.is_keywordable_ident_where(Ident::is_path_segment_keyword)
}

/// Returns true for reserved identifiers used internally for elided lifetimes,
/// unnamed method parameters, crate root module, error recovery etc.
pub fn is_special_ident(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_special)
self.is_keywordable_ident_where(Ident::is_special)
}

/// Returns `true` if the token is a keyword used in the language.
pub fn is_used_keyword(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_used_keyword)
self.is_keywordable_ident_where(Ident::is_used_keyword)
}

/// Returns `true` if the token is a keyword reserved for possible future use.
pub fn is_unused_keyword(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_unused_keyword)
self.is_keywordable_ident_where(Ident::is_unused_keyword)
}

/// Returns `true` if the token is either a special identifier or a keyword.
pub fn is_reserved_ident(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_reserved)
self.is_keywordable_ident_where(Ident::is_reserved)
}

/// Returns `true` if the token is the identifier `true` or `false`.
pub fn is_bool_lit(&self) -> bool {
self.is_non_raw_ident_where(|id| id.name.is_bool_lit())
self.is_keywordable_ident_where(|id| id.name.is_bool_lit())
}

pub fn is_numeric_lit(&self) -> bool {
Expand All @@ -753,9 +791,9 @@ impl Token {
}

/// Returns `true` if the token is a non-raw identifier for which `pred` holds.
pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
pub fn is_keywordable_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
match self.ident() {
Some((id, false)) => pred(id),
Some((id, IdentKind::Default | IdentKind::Keyword)) => pred(id),
_ => false,
}
}
Expand Down Expand Up @@ -806,7 +844,7 @@ impl Token {
_ => return None,
},
SingleQuote => match joint.kind {
Ident(name, false) => Lifetime(Symbol::intern(&format!("'{name}"))),
Ident(name, IdentKind::Default) => Lifetime(Symbol::intern(&format!("'{name}"))),
_ => return None,
},

Expand Down Expand Up @@ -836,7 +874,7 @@ pub enum Nonterminal {
NtPat(P<ast::Pat>),
NtExpr(P<ast::Expr>),
NtTy(P<ast::Ty>),
NtIdent(Ident, /* is_raw */ bool),
NtIdent(Ident, IdentKind),
NtLifetime(Ident),
NtLiteral(P<ast::Expr>),
/// Stuff inside brackets for attributes
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

use crate::ast::{AttrStyle, StmtKind};
use crate::ast_traits::{HasAttrs, HasSpan, HasTokens};
use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind};
use crate::token::{self, Delimiter, IdentKind, Nonterminal, Token, TokenKind};
use crate::AttrVec;

use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
Expand Down Expand Up @@ -677,7 +677,7 @@ impl TokenStream {
DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
Delimiter::Bracket,
[
TokenTree::token_alone(token::Ident(sym::doc, false), span),
TokenTree::token_alone(token::Ident(sym::doc, IdentKind::Default), span),
TokenTree::token_alone(token::Eq, span),
TokenTree::token_alone(
TokenKind::lit(token::StrRaw(num_of_hashes), data, None),
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,9 +547,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
visitor.visit_path(path, pattern.id);
walk_list!(visitor, visit_pat_field, fields);
}
PatKind::Box(subpattern) | PatKind::Ref(subpattern, _) | PatKind::Paren(subpattern) => {
visitor.visit_pat(subpattern)
}
PatKind::Box(subpattern)
| PatKind::Ref(subpattern, _)
| PatKind::Paren(subpattern)
| PatKind::Deref(subpattern) => visitor.visit_pat(subpattern),
PatKind::Ident(_, ident, optional_subpattern) => {
visitor.visit_ident(*ident);
walk_list!(visitor, visit_pat, optional_subpattern);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
PatKind::Ref(inner, mutbl) => {
break hir::PatKind::Ref(self.lower_pat(inner), *mutbl);
}
PatKind::Deref(inner) => {
break hir::PatKind::Deref(self.lower_pat(inner));
}
PatKind::Range(e1, e2, Spanned { node: end, .. }) => {
break hir::PatKind::Range(
e1.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
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 @@ -516,6 +516,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
}
};
}
gate_all!(forced_keywords, "`k#ident` syntax is experimental");
gate_all!(
if_let_guard,
"`if let` guards are experimental",
Expand Down
14 changes: 10 additions & 4 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);

fn print_ident(&mut self, ident: Ident) {
self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
self.word(
IdentPrinter::for_ast_ident(ident, ident.is_raw_guess().then(|| "r#")).to_string(),
);
self.ann_post(ident)
}

Expand Down Expand Up @@ -715,7 +717,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
token::NtBlock(e) => self.block_to_string(e),
token::NtStmt(e) => self.stmt_to_string(e),
token::NtPat(e) => self.pat_to_string(e),
token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(*e, *is_raw).to_string(),
&token::NtIdent(e, kind) => IdentPrinter::for_ast_ident(e, kind.prefix()).to_string(),
token::NtLifetime(e) => e.to_string(),
token::NtLiteral(e) => self.expr_to_string(e),
token::NtVis(e) => self.vis_to_string(e),
Expand Down Expand Up @@ -778,8 +780,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
token::Literal(lit) => literal_to_string(lit).into(),

/* Name components */
token::Ident(s, is_raw) => {
IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string().into()
token::Ident(s, kind) => {
IdentPrinter::new(s, kind.prefix(), convert_dollar_crate).to_string().into()
}
token::Lifetime(s) => s.to_string().into(),

Expand Down Expand Up @@ -1519,6 +1521,10 @@ impl<'a> State<'a> {
self.pclose();
}
PatKind::MacCall(m) => self.print_mac(m),
PatKind::Deref(inner) => {
self.word("k#deref ");
self.print_pat(inner);
}
}
self.ann.post(self, AnnNode::Pat(pat))
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::token::{self, Delimiter, IdentKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::PResult;
Expand Down Expand Up @@ -416,7 +416,7 @@ fn parse_reg<'a>(
) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
let result = match p.token.uninterpolate().kind {
token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
token::Ident(name, IdentKind::Default) => ast::InlineAsmRegOrRegClass::RegClass(name),
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
*explicit_reg = true;
ast::InlineAsmRegOrRegClass::Reg(symbol)
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rustc_ast::{
ptr::P,
token,
token::Delimiter,
token::{Delimiter, IdentKind},
tokenstream::{DelimSpan, TokenStream, TokenTree},
BinOpKind, BorrowKind, DelimArgs, Expr, ExprKind, ItemKind, MacCall, MethodCall, Mutability,
Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind, DUMMY_NODE_ID,
Expand Down Expand Up @@ -170,7 +170,10 @@ impl<'cx, 'a> Context<'cx, 'a> {
];
let captures = self.capture_decls.iter().flat_map(|cap| {
[
TokenTree::token_joint_hidden(token::Ident(cap.ident.name, false), cap.ident.span),
TokenTree::token_joint_hidden(
token::Ident(cap.ident.name, IdentKind::Default),
cap.ident.span,
),
TokenTree::token_alone(token::Comma, self.span),
]
});
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_expand/src/mbe/macro_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
use crate::errors;
use crate::mbe::{KleeneToken, TokenTree};

use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::token::{Delimiter, IdentKind, Token, TokenKind};
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{DiagnosticMessage, MultiSpan};
Expand Down Expand Up @@ -409,7 +409,10 @@ fn check_nested_occurrences(
match (state, tt) {
(
NestedMacroState::Empty,
&TokenTree::Token(Token { kind: TokenKind::Ident(name, false), .. }),
&TokenTree::Token(Token {
kind: TokenKind::Ident(name, IdentKind::Default | IdentKind::Keyword),
..
}),
) => {
if name == kw::MacroRules {
state = NestedMacroState::MacroRules;
Expand Down
Loading
Loading