From 6debdc9735e81b552d9d98f29d639cfdd6065dec Mon Sep 17 00:00:00 2001
From: David Tolnay <dtolnay@gmail.com>
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<Attribute>)) {
         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<LazyTokenStream>> {
         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<T: MutVisitor>(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<ast::Ty>),
     NtIdent(Ident, /* is_raw */ bool),
     NtLifetime(Ident),
-    NtLiteral(P<ast::Expr>),
+    NtLiteral(SignedLiteral),
     /// Stuff inside brackets for attributes
     NtMeta(P<ast::AttrItem>),
     NtPath(ast::Path),
@@ -687,6 +687,15 @@ pub enum Nonterminal {
     NtTT(TokenTree),
 }
 
+#[derive(Clone, Encodable, Decodable)]
+pub struct SignedLiteral {
+    pub neg: Option<Span>,
+    pub lit: P<ast::Lit>,
+    // 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<Target = pp::Printer> + 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<Expr> {
+    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 => {