Implement our own small-integer optimization (#7584)

## Summary

This is a follow-up to #7469 that attempts to achieve similar gains, but
without introducing malachite. Instead, this PR removes the `BigInt`
type altogether, instead opting for a simple enum that allows us to
store small integers directly and only allocate for values greater than
`i64`:

```rust
/// A Python integer literal. Represents both small (fits in an `i64`) and large integers.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Int(Number);

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Number {
    /// A "small" number that can be represented as an `i64`.
    Small(i64),
    /// A "large" number that cannot be represented as an `i64`.
    Big(Box<str>),
}

impl std::fmt::Display for Number {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Number::Small(value) => write!(f, "{value}"),
            Number::Big(value) => write!(f, "{value}"),
        }
    }
}
```

We typically don't care about numbers greater than `isize` -- our only
uses are comparisons against small constants (like `1`, `2`, `3`, etc.),
so there's no real loss of information, except in one or two rules where
we're now a little more conservative (with the worst-case being that we
don't flag, e.g., an `itertools.pairwise` that uses an extremely large
value for the slice start constant). For simplicity, a few diagnostics
now show a dedicated message when they see integers that are out of the
supported range (e.g., `outdated-version-block`).

An additional benefit here is that we get to remove a few dependencies,
especially `num-bigint`.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-09-25 11:13:21 -04:00 committed by GitHub
parent 65aebf127a
commit 93b5d8a0fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 707 additions and 385 deletions

View file

@ -1,8 +1,7 @@
// auto-generated: "lalrpop 0.20.0"
// sha3: eb535c9ae34baad8c940ef61dbbea0a7fec7baf3cd62af40837b2616f656f927
use num_bigint::BigInt;
// sha3: 8fa4c9e4c8c7df1e71b915249df9a6cd968890e1c6be3b3dc389ced5be3a3281
use ruff_text_size::{Ranged, TextSize};
use ruff_python_ast::{self as ast, IpyEscapeKind};
use ruff_python_ast::{self as ast, Int, IpyEscapeKind};
use crate::{
Mode,
lexer::{LexicalError, LexicalErrorType},
@ -23,9 +22,8 @@ extern crate alloc;
#[allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports, unused_parens, clippy::all)]
mod __parse__Top {
use num_bigint::BigInt;
use ruff_text_size::{Ranged, TextSize};
use ruff_python_ast::{self as ast, IpyEscapeKind};
use ruff_python_ast::{self as ast, Int, IpyEscapeKind};
use crate::{
Mode,
lexer::{LexicalError, LexicalErrorType},
@ -48,7 +46,7 @@ mod __parse__Top {
Variant0(token::Tok),
Variant1((f64, f64)),
Variant2(f64),
Variant3(BigInt),
Variant3(Int),
Variant4((IpyEscapeKind, String)),
Variant5(String),
Variant6((String, StringKind, bool)),
@ -17716,7 +17714,7 @@ mod __parse__Top {
fn __pop_Variant3<
>(
__symbols: &mut alloc::vec::Vec<(TextSize,__Symbol<>,TextSize)>
) -> (TextSize, BigInt, TextSize)
) -> (TextSize, Int, TextSize)
{
match __symbols.pop() {
Some((__l, __Symbol::Variant3(__v), __r)) => (__l, __v, __r),
@ -34480,7 +34478,7 @@ fn __action232<
fn __action233<
>(
mode: Mode,
(_, value, _): (TextSize, BigInt, TextSize),
(_, value, _): (TextSize, Int, TextSize),
) -> ast::Constant
{
ast::Constant::Int(value)