From 6debdc9735e81b552d9d98f29d639cfdd6065dec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 28 Dec 2021 19:33:45 -0800 Subject: [PATCH] Store NtLiteral without generalizing to Expr --- compiler/rustc_ast/src/ast_like.rs | 17 ++++--- compiler/rustc_ast/src/mut_visit.rs | 8 ++- compiler/rustc_ast/src/token.rs | 14 +++++- compiler/rustc_ast/src/util/literal.rs | 6 ++- compiler/rustc_ast_pretty/src/pprust/state.rs | 8 ++- compiler/rustc_parse/src/lib.rs | 13 +++-- compiler/rustc_parse/src/parser/expr.rs | 50 ++++++++++++------- .../rustc_parse/src/parser/nonterminal.rs | 28 ++++++++--- 8 files changed, 106 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs index b9c397974a163..5186a88a918e3 100644 --- a/compiler/rustc_ast/src/ast_like.rs +++ b/compiler/rustc_ast/src/ast_like.rs @@ -44,7 +44,7 @@ impl AstLike for crate::token::Nonterminal { match self { Nonterminal::NtItem(item) => item.attrs(), Nonterminal::NtStmt(stmt) => stmt.attrs(), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.attrs(), + Nonterminal::NtExpr(expr) => expr.attrs(), Nonterminal::NtPat(_) | Nonterminal::NtTy(_) | Nonterminal::NtMeta(_) @@ -53,14 +53,15 @@ impl AstLike for crate::token::Nonterminal { | Nonterminal::NtTT(_) | Nonterminal::NtBlock(_) | Nonterminal::NtIdent(..) - | Nonterminal::NtLifetime(_) => &[], + | Nonterminal::NtLifetime(_) + | Nonterminal::NtLiteral(_) => &[], } } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { match self { Nonterminal::NtItem(item) => item.visit_attrs(f), Nonterminal::NtStmt(stmt) => stmt.visit_attrs(f), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.visit_attrs(f), + Nonterminal::NtExpr(expr) => expr.visit_attrs(f), Nonterminal::NtPat(_) | Nonterminal::NtTy(_) | Nonterminal::NtMeta(_) @@ -69,21 +70,25 @@ impl AstLike for crate::token::Nonterminal { | Nonterminal::NtTT(_) | Nonterminal::NtBlock(_) | Nonterminal::NtIdent(..) - | Nonterminal::NtLifetime(_) => {} + | Nonterminal::NtLifetime(_) + | Nonterminal::NtLiteral(_) => {} } } fn tokens_mut(&mut self) -> Option<&mut Option> { match self { Nonterminal::NtItem(item) => item.tokens_mut(), Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), + Nonterminal::NtExpr(expr) => expr.tokens_mut(), Nonterminal::NtPat(pat) => pat.tokens_mut(), Nonterminal::NtTy(ty) => ty.tokens_mut(), Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), Nonterminal::NtPath(path) => path.tokens_mut(), Nonterminal::NtVis(vis) => vis.tokens_mut(), Nonterminal::NtBlock(block) => block.tokens_mut(), - Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) | Nonterminal::NtTT(..) => None, + Nonterminal::NtIdent(..) + | Nonterminal::NtLifetime(..) + | Nonterminal::NtLiteral(_) + | Nonterminal::NtTT(..) => None, } } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 6bf23af81bf6b..ef511aeaa2bf3 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -781,7 +781,13 @@ pub fn visit_interpolated(nt: &mut token::Nonterminal, vis: &mut token::NtTy(ty) => vis.visit_ty(ty), token::NtIdent(ident, _is_raw) => vis.visit_ident(ident), token::NtLifetime(ident) => vis.visit_ident(ident), - token::NtLiteral(expr) => vis.visit_expr(expr), + token::NtLiteral(signed) => { + if let Some(neg_span) = &mut signed.neg { + vis.visit_span(neg_span); + } + vis.visit_span(&mut signed.lit.span); + vis.visit_span(&mut signed.span); + } token::NtMeta(item) => { let AttrItem { path, args, tokens } = item.deref_mut(); vis.visit_path(path); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index db066d7c6a519..714890978f695 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -679,7 +679,7 @@ pub enum Nonterminal { NtTy(P), NtIdent(Ident, /* is_raw */ bool), NtLifetime(Ident), - NtLiteral(P), + NtLiteral(SignedLiteral), /// Stuff inside brackets for attributes NtMeta(P), NtPath(ast::Path), @@ -687,6 +687,15 @@ pub enum Nonterminal { NtTT(TokenTree), } +#[derive(Clone, Encodable, Decodable)] +pub struct SignedLiteral { + pub neg: Option, + pub lit: P, + // If neg is None, then identical to lit.span. + // If neg is Some, then neg.to(lit.span). + pub span: Span, +} + // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Nonterminal, 48); @@ -776,9 +785,10 @@ impl Nonterminal { NtBlock(block) => block.span, NtStmt(stmt) => stmt.span, NtPat(pat) => pat.span, - NtExpr(expr) | NtLiteral(expr) => expr.span, + NtExpr(expr) => expr.span, NtTy(ty) => ty.span, NtIdent(ident, _) | NtLifetime(ident) => ident.span, + NtLiteral(lit) => lit.span, NtMeta(attr_item) => attr_item.span(), NtPath(path) => path.span, NtVis(vis) => vis.span, diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 1cc5ddfd8ee29..2c22c597ff996 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -217,10 +217,14 @@ impl Lit { } token::Literal(lit) => lit, token::Interpolated(ref nt) => { - if let token::NtExpr(expr) | token::NtLiteral(expr) = &**nt { + if let token::NtExpr(expr) = &**nt { if let ast::ExprKind::Lit(lit) = &expr.kind { return Ok(lit.clone()); } + } else if let token::NtLiteral(signed) = &**nt { + if signed.neg.is_none() { + return Ok((*signed.lit).clone()); + } } return Err(LitError::NotLiteral); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 6c5b38bc4bb15..d72f552c6c4a1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -706,7 +706,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere token::NtPat(ref e) => self.pat_to_string(e), token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(), token::NtLifetime(e) => e.to_string(), - token::NtLiteral(ref e) => self.expr_to_string(e), + token::NtLiteral(ref e) => { + let mut string = literal_to_string(e.lit.token); + if e.neg.is_some() { + string.insert(0, '-'); + } + string + } token::NtTT(ref tree) => self.tt_to_string(tree), token::NtVis(ref e) => self.vis_to_string(e), } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2b1b2f3fce496..5e4cff901d04d 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -10,7 +10,7 @@ extern crate tracing; use rustc_ast as ast; -use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{self, BinOpToken, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{self, AttributesData, CanSynthesizeMissingTokens, LazyTokenStream}; use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; use rustc_ast::tokenstream::{Spacing, TokenStream}; @@ -288,8 +288,15 @@ pub fn nt_to_tokenstream( Nonterminal::NtPath(ref path) => convert_tokens(path.tokens.as_ref()), Nonterminal::NtVis(ref vis) => convert_tokens(vis.tokens.as_ref()), Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), - Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { - prepend_attrs(&expr.attrs, expr.tokens.as_ref()) + Nonterminal::NtExpr(ref expr) => prepend_attrs(&expr.attrs, expr.tokens.as_ref()), + Nonterminal::NtLiteral(ref signed) => { + let literal_tt = tokenstream::TokenTree::token(token::Literal(signed.lit.token), signed.lit.span); + if let Some(neg_span) = signed.neg { + let neg_tt = tokenstream::TokenTree::token(token::BinOp(BinOpToken::Minus), neg_span); + Some(TokenStream::new(vec![(neg_tt, Spacing::Alone), (literal_tt, Spacing::Alone)])) + } else { + Some(literal_tt.into()) + } } }; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f706a98a4fcfa..aa8f41a1bf277 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -8,7 +8,7 @@ use crate::maybe_recover_from_interpolated_ty_qpath; use ast::token::DelimToken; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Token, TokenKind}; +use rustc_ast::token::{self, SignedLiteral, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; @@ -35,11 +35,16 @@ macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = &$p.token.kind { match &**nt { - token::NtExpr(e) | token::NtLiteral(e) => { + token::NtExpr(e) => { let e = e.clone(); $p.bump(); return Ok(e); } + token::NtLiteral(lit) => { + let e = signed_lit_to_expr(lit.clone()); + $p.bump(); + return Ok(e); + } token::NtPath(path) => { let path = path.clone(); $p.bump(); @@ -1609,12 +1614,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { self.parse_opt_lit().ok_or_else(|| { if let token::Interpolated(inner) = &self.token.kind { - let expr = match inner.as_ref() { - token::NtExpr(expr) => Some(expr), - token::NtLiteral(expr) => Some(expr), - _ => None, - }; - if let Some(expr) = expr { + if let token::NtExpr(expr) = inner.as_ref() { if matches!(expr.kind, ExprKind::Err) { self.diagnostic() .delay_span_bug(self.token.span, &"invalid interpolated expression"); @@ -1800,17 +1800,10 @@ impl<'a> Parser<'a> { let lo = self.token.span; let minus_present = self.eat(&token::BinOp(token::Minus)); let lit = self.parse_lit()?; - let expr = self.mk_expr(lit.span, ExprKind::Lit(lit), AttrVec::new()); + let (neg, span) = + if minus_present { (Some(lo), lo.to(self.prev_token.span)) } else { (None, lit.span) }; - if minus_present { - Ok(self.mk_expr( - lo.to(self.prev_token.span), - self.mk_unary(UnOp::Neg, expr), - AttrVec::new(), - )) - } else { - Ok(expr) - } + Ok(signed_lit_to_expr(SignedLiteral { neg, lit: P(lit), span })) } fn is_array_like_block(&mut self) -> bool { @@ -2916,3 +2909,24 @@ impl<'a> Parser<'a> { }) } } + +fn signed_lit_to_expr(signed: SignedLiteral) -> P { + let expr = P(Expr { + kind: ExprKind::Lit((*signed.lit).clone()), + span: signed.lit.span, + attrs: AttrVec::new(), + id: DUMMY_NODE_ID, + tokens: None, + }); + if let Some(neg_span) = signed.neg { + P(Expr { + kind: ExprKind::Unary(UnOp::Neg, expr), + span: neg_span.to(signed.lit.span), + attrs: AttrVec::new(), + id: DUMMY_NODE_ID, + tokens: None, + }) + } else { + expr + } +} diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 72e6f8a1bc857..ee7fffabf330c 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,9 +1,10 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Nonterminal, NonterminalKind, Token}; -use rustc_ast::AstLike; +use rustc_ast::token::{self, Nonterminal, NonterminalKind, SignedLiteral, Token}; +use rustc_ast::{AstLike, ExprKind, UnOp}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; +use rustc_span::BytePos; use crate::parser::pat::{RecoverColon, RecoverComma}; use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle}; @@ -133,10 +134,25 @@ impl<'a> Parser<'a> { NonterminalKind::Expr => token::NtExpr(self.parse_expr_force_collect()?), NonterminalKind::Literal => { - // The `:literal` matcher does not support attributes - token::NtLiteral( - self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?, - ) + let mut expr = self.parse_literal_maybe_minus()?.into_inner(); + let span = expr.span; + + let mut neg = None; + if let ExprKind::Unary(UnOp::Neg, inner) = expr.kind { + // ast::Expr does not store an individual Span of the minus sign, + // only the Span of the whole expr, but we can reconstruct it. + let span_data = expr.span.data(); + neg = Some(span_data.with_hi(BytePos(span_data.lo.0 + 1))); + expr = inner.into_inner(); + } + + let lit = if let ExprKind::Lit(lit) = expr.kind { + P(lit) + } else { + return Err(self.struct_span_err(expr.span, "expected a literal")); + }; + + token::NtLiteral(SignedLiteral { neg, lit, span }) } NonterminalKind::Ty => {