Skip to content
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
33 changes: 29 additions & 4 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1869,16 +1869,31 @@ impl StrLit {
}
}

#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq)]
#[derive(HashStable_Generic)]
pub enum Sign {
/// A `-` in front of a literal.
Neg,
/// No sign. There are no leading `+` in front of literals.
None,
}

impl Sign {
pub fn is_neg(&self) -> bool {
matches!(self, Self::Neg)
}
}

/// Type of the integer literal based on provided suffix.
#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq)]
#[derive(HashStable_Generic)]
pub enum LitIntType {
/// e.g. `42_i32`.
Signed(IntTy),
Signed(IntTy, Sign),
/// e.g. `42_u32`.
Unsigned(UintTy),
/// e.g. `42`.
Unsuffixed,
Unsuffixed(Sign),
}

/// Type of the float literal based on provided suffix.
Expand Down Expand Up @@ -1913,7 +1928,7 @@ pub enum LitKind {
Char(char),
/// An integer literal (`1`).
Int(Pu128, LitIntType),
/// A float literal (`1.0`, `1f64` or `1E10f64`). The pre-suffix part is
/// A float literal (`1.0`, `-1.0`, `1f64` or `1E10f64`). The pre-suffix part is
/// stored as a symbol rather than `f64` so that `LitKind` can impl `Eq`
/// and `Hash`.
Float(Symbol, LitFloatType),
Expand Down Expand Up @@ -1964,12 +1979,22 @@ impl LitKind {
| LitKind::CStr(..)
| LitKind::Byte(..)
| LitKind::Char(..)
| LitKind::Int(_, LitIntType::Unsuffixed)
| LitKind::Int(_, LitIntType::Unsuffixed(_))
| LitKind::Float(_, LitFloatType::Unsuffixed)
| LitKind::Bool(..)
| LitKind::Err(_) => false,
}
}

pub fn is_negative(&self) -> bool {
match self {
LitKind::Int(_, LitIntType::Signed(_, sign) | LitIntType::Unsuffixed(sign)) => {
sign.is_neg()
}
LitKind::Float(f, _) => f.as_str().starts_with('-'),
_ => false,
}
}
}

// N.B., If you change this, you'll probably want to change the corresponding
Expand Down
72 changes: 51 additions & 21 deletions compiler/rustc_ast/src/util/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_lexer::unescape::{
use rustc_span::{Span, Symbol, kw, sym};
use tracing::debug;

use crate::Sign;
use crate::ast::{self, LitKind, MetaItemLit, StrStyle};
use crate::token::{self, Token};

Expand Down Expand Up @@ -36,20 +37,32 @@ pub enum LitError {
InvalidSuffix(Symbol),
InvalidIntSuffix(Symbol),
InvalidFloatSuffix(Symbol),
InvalidNegation(token::LitKind),
NonDecimalFloat(u32), // u32 is the base
IntTooLarge(u32), // u32 is the base
}

impl LitKind {
/// Converts literal token into a semantic literal.
pub fn from_token_lit(lit: token::Lit) -> Result<LitKind, LitError> {
Self::from_token_lit_maybe_negated(lit, Sign::None)
}

/// Converts literal token into a semantic literal.
/// May optionally include a sign, and will error if a literal
/// other than integer or float literals is negated.
pub fn from_token_lit_maybe_negated(lit: token::Lit, sign: Sign) -> Result<LitKind, LitError> {
let token::Lit { kind, symbol, suffix } = lit;
if let Some(suffix) = suffix
&& !kind.may_have_suffix()
{
return Err(LitError::InvalidSuffix(suffix));
}

if sign.is_neg() && !matches!(kind, token::Integer | token::Float) {
return Err(LitError::InvalidNegation(kind));
}

// For byte/char/string literals, chars and escapes have already been
// checked in the lexer (in `cook_lexer_literal`). So we can assume all
// chars and escapes are valid here.
Expand All @@ -71,8 +84,8 @@ impl LitKind {

// There are some valid suffixes for integer and float literals,
// so all the handling is done internally.
token::Integer => return integer_lit(symbol, suffix),
token::Float => return float_lit(symbol, suffix),
token::Integer => return integer_lit(symbol, suffix, sign),
token::Float => return float_lit(symbol, suffix, sign),

token::Str => {
// If there are no characters requiring special treatment we can
Expand Down Expand Up @@ -189,14 +202,21 @@ impl fmt::Display for LitKind {
let symbol = str::from_utf8(bytes).unwrap();
write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize),)?;
}
LitKind::Int(n, ty) => {
write!(f, "{n}")?;
match ty {
ast::LitIntType::Unsigned(ty) => write!(f, "{}", ty.name())?,
ast::LitIntType::Signed(ty) => write!(f, "{}", ty.name())?,
ast::LitIntType::Unsuffixed => {}
LitKind::Int(n, ty) => match ty {
ast::LitIntType::Unsigned(ty) => write!(f, "{n}{}", ty.name())?,
ast::LitIntType::Signed(ty, sign) => {
if sign.is_neg() {
write!(f, "-")?;
}
write!(f, "{n}{}", ty.name())?
}
}
ast::LitIntType::Unsuffixed(sign) => {
if sign.is_neg() {
write!(f, "-")?;
}
write!(f, "{n}")?;
}
},
LitKind::Float(symbol, ty) => {
write!(f, "{symbol}")?;
match ty {
Expand Down Expand Up @@ -269,11 +289,14 @@ fn filtered_float_lit(
symbol: Symbol,
suffix: Option<Symbol>,
base: u32,
sign: Sign,
) -> Result<LitKind, LitError> {
debug!("filtered_float_lit: {:?}, {:?}, {:?}", symbol, suffix, base);
debug!(?symbol, ?suffix, ?base, ?sign);
if base != 10 {
return Err(LitError::NonDecimalFloat(base));
}
let symbol =
if sign.is_neg() { Symbol::intern(&format!("-{}", symbol.as_str())) } else { symbol };
Ok(match suffix {
Some(suffix) => LitKind::Float(
symbol,
Expand All @@ -289,12 +312,12 @@ fn filtered_float_lit(
})
}

fn float_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitError> {
fn float_lit(symbol: Symbol, suffix: Option<Symbol>, sign: Sign) -> Result<LitKind, LitError> {
debug!("float_lit: {:?}, {:?}", symbol, suffix);
filtered_float_lit(strip_underscores(symbol), suffix, 10)
filtered_float_lit(strip_underscores(symbol), suffix, 10, sign)
}

fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitError> {
fn integer_lit(symbol: Symbol, suffix: Option<Symbol>, sign: Sign) -> Result<LitKind, LitError> {
debug!("integer_lit: {:?}, {:?}", symbol, suffix);
let symbol = strip_underscores(symbol);
let s = symbol.as_str();
Expand All @@ -308,12 +331,12 @@ fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitErr

let ty = match suffix {
Some(suf) => match suf {
sym::isize => ast::LitIntType::Signed(ast::IntTy::Isize),
sym::i8 => ast::LitIntType::Signed(ast::IntTy::I8),
sym::i16 => ast::LitIntType::Signed(ast::IntTy::I16),
sym::i32 => ast::LitIntType::Signed(ast::IntTy::I32),
sym::i64 => ast::LitIntType::Signed(ast::IntTy::I64),
sym::i128 => ast::LitIntType::Signed(ast::IntTy::I128),
sym::isize => ast::LitIntType::Signed(ast::IntTy::Isize, sign),
sym::i8 => ast::LitIntType::Signed(ast::IntTy::I8, sign),
sym::i16 => ast::LitIntType::Signed(ast::IntTy::I16, sign),
sym::i32 => ast::LitIntType::Signed(ast::IntTy::I32, sign),
sym::i64 => ast::LitIntType::Signed(ast::IntTy::I64, sign),
sym::i128 => ast::LitIntType::Signed(ast::IntTy::I128, sign),
sym::usize => ast::LitIntType::Unsigned(ast::UintTy::Usize),
sym::u8 => ast::LitIntType::Unsigned(ast::UintTy::U8),
sym::u16 => ast::LitIntType::Unsigned(ast::UintTy::U16),
Expand All @@ -322,11 +345,18 @@ fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitErr
sym::u128 => ast::LitIntType::Unsigned(ast::UintTy::U128),
// `1f64` and `2f32` etc. are valid float literals, and
// `fxxx` looks more like an invalid float literal than invalid integer literal.
_ if suf.as_str().starts_with('f') => return filtered_float_lit(symbol, suffix, base),
_ if suf.as_str().starts_with('f') => {
return filtered_float_lit(symbol, suffix, base, sign);
}
_ => return Err(LitError::InvalidIntSuffix(suf)),
},
_ => ast::LitIntType::Unsuffixed,
_ => ast::LitIntType::Unsuffixed(sign),
};
if let ast::LitIntType::Unsigned(_) = ty
&& sign.is_neg()
{
return Err(LitError::InvalidNegation(token::Integer));
}

let s = &s[if base != 10 { 2 } else { 0 }..];
u128::from_str_radix(s, base)
Expand Down
20 changes: 18 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Binary(binop, lhs, rhs)
}
ExprKind::Unary(op, ohs) => {
if let UnOp::Neg = op {
if let ExprKind::Lit(token_lit) = &ohs.kind {
return hir::Expr {
hir_id: expr_hir_id,
kind: hir::ExprKind::Lit(self.lower_lit(
token_lit,
e.span,
Sign::Neg,
)),
span: self.lower_span(e.span),
};
}
}
let op = self.lower_unop(*op);
let ohs = self.lower_expr(ohs);
hir::ExprKind::Unary(op, ohs)
}
ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)),
ExprKind::Lit(token_lit) => {
hir::ExprKind::Lit(self.lower_lit(token_lit, e.span, Sign::None))
}
ExprKind::IncludedBytes(bytes) => {
let lit = self.arena.alloc(respan(
self.lower_span(e.span),
Expand Down Expand Up @@ -419,8 +434,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
token_lit: &token::Lit,
span: Span,
sign: Sign,
) -> &'hir Spanned<LitKind> {
let lit_kind = match LitKind::from_token_lit(*token_lit) {
let lit_kind = match LitKind::from_token_lit_maybe_negated(*token_lit, sign) {
Ok(lit_kind) => lit_kind,
Err(err) => {
let guar = report_lit_error(&self.tcx.sess.psess, err, *token_lit, span);
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_ast_lowering/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
Ok(LitKind::Int(n, ty)) => {
match ty {
// unsuffixed integer literals are assumed to be i32's
LitIntType::Unsuffixed => {
(n <= i32::MAX as u128).then_some(Symbol::intern(&n.to_string()))
}
LitIntType::Signed(int_ty) => {
LitIntType::Unsuffixed(sign) => (matches!(sign, Sign::None)
&& n <= i32::MAX as u128)
.then_some(Symbol::intern(&n.to_string())),
LitIntType::Signed(int_ty, sign) => {
let max_literal = self.int_ty_max(int_ty);
(n <= max_literal).then_some(Symbol::intern(&n.to_string()))
(matches!(sign, Sign::None) && n <= max_literal)
.then_some(Symbol::intern(&n.to_string()))
}
LitIntType::Unsigned(uint_ty) => {
let max_literal = self.uint_ty_max(uint_ty);
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,18 +391,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let span = self.lower_span(expr.span);
let err = |guar| hir::PatExprKind::Lit {
lit: self.arena.alloc(respan(span, LitKind::Err(guar))),
negated: false,
};
let kind = match &expr.kind {
ExprKind::Lit(lit) => {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false }
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span, Sign::None) }
}
ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)),
ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit {
lit: self
.arena
.alloc(respan(span, LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked))),
negated: false,
},
ExprKind::Err(guar) => err(*guar),
ExprKind::Dummy => span_bug!(span, "lowered ExprKind::Dummy"),
Expand All @@ -416,7 +414,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
None,
)),
ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true }
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span, Sign::Neg) }
}
_ => {
let pattern_from_macro = expr.is_approximately_pattern();
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/repr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_abi::Align;
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
use rustc_ast::{IntTy, LitIntType, LitKind, Sign, UintTy};
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
use rustc_span::{Span, Symbol, sym};

Expand Down Expand Up @@ -225,7 +225,7 @@ fn parse_repr_align(
}

fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
if let LitKind::Int(literal, LitIntType::Unsuffixed(Sign::None)) = node {
// `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
if literal.get().is_power_of_two() {
// Only possible error is larger than 2^29
Expand Down
14 changes: 8 additions & 6 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> {
name: &'a str,

#[skip_arg]
int: u128,
int: String,
},

#[suggestion(
Expand All @@ -342,11 +342,13 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> {

impl<'a> IncorrectReprFormatGenericCause<'a> {
pub(crate) fn from_lit_kind(span: Span, kind: &ast::LitKind, name: &'a str) -> Option<Self> {
match kind {
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
Some(Self::Int { span, name, int: int.get() })
}
ast::LitKind::Str(symbol, _) => Some(Self::Symbol { span, name, symbol: *symbol }),
match *kind {
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed(sign)) => Some(Self::Int {
span,
name,
int: if sign.is_neg() { format!("-{}", int.get()) } else { int.get().to_string() },
}),
ast::LitKind::Str(symbol, _) => Some(Self::Symbol { span, name, symbol }),
_ => None,
}
}
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_builtin_macros/src/concat_bytes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_ast::ptr::P;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{ExprKind, LitIntType, LitKind, UintTy, token};
use rustc_ast::{ExprKind, LitIntType, LitKind, Sign, UintTy, token};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
use rustc_session::errors::report_lit_error;
use rustc_span::{ErrorGuaranteed, Span};
Expand Down Expand Up @@ -51,7 +51,10 @@ fn invalid_type_err(
snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
}
Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
Ok(LitKind::Int(
val,
LitIntType::Unsuffixed(Sign::None) | LitIntType::Unsigned(UintTy::U8),
)) => {
assert!(val.get() > u8::MAX.into()); // must be an error
dcx.emit_err(ConcatBytesOob { span })
}
Expand Down Expand Up @@ -79,7 +82,7 @@ fn handle_array_element(
match LitKind::from_token_lit(token_lit) {
Ok(LitKind::Int(
val,
LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8),
LitIntType::Unsuffixed(Sign::None) | LitIntType::Unsigned(UintTy::U8),
)) if let Ok(val) = u8::try_from(val.get()) => {
return Some(val);
}
Expand Down
Loading
Loading