133d722a9Sopenharmony_ciuse crate::syntax::Atom::{self, *};
233d722a9Sopenharmony_ciuse proc_macro2::{Literal, Span, TokenStream};
333d722a9Sopenharmony_ciuse quote::ToTokens;
433d722a9Sopenharmony_ciuse std::cmp::Ordering;
533d722a9Sopenharmony_ciuse std::collections::BTreeSet;
633d722a9Sopenharmony_ciuse std::fmt::{self, Display};
733d722a9Sopenharmony_ciuse std::str::FromStr;
833d722a9Sopenharmony_ciuse std::u64;
933d722a9Sopenharmony_ciuse syn::{Error, Expr, Lit, Result, Token, UnOp};
1033d722a9Sopenharmony_ci
1133d722a9Sopenharmony_cipub struct DiscriminantSet {
1233d722a9Sopenharmony_ci    repr: Option<Atom>,
1333d722a9Sopenharmony_ci    values: BTreeSet<Discriminant>,
1433d722a9Sopenharmony_ci    previous: Option<Discriminant>,
1533d722a9Sopenharmony_ci}
1633d722a9Sopenharmony_ci
1733d722a9Sopenharmony_ci#[derive(Copy, Clone, Eq, PartialEq)]
1833d722a9Sopenharmony_cipub struct Discriminant {
1933d722a9Sopenharmony_ci    sign: Sign,
2033d722a9Sopenharmony_ci    magnitude: u64,
2133d722a9Sopenharmony_ci}
2233d722a9Sopenharmony_ci
2333d722a9Sopenharmony_ci#[derive(Copy, Clone, Eq, PartialEq)]
2433d722a9Sopenharmony_cienum Sign {
2533d722a9Sopenharmony_ci    Negative,
2633d722a9Sopenharmony_ci    Positive,
2733d722a9Sopenharmony_ci}
2833d722a9Sopenharmony_ci
2933d722a9Sopenharmony_ciimpl DiscriminantSet {
3033d722a9Sopenharmony_ci    pub fn new(repr: Option<Atom>) -> Self {
3133d722a9Sopenharmony_ci        DiscriminantSet {
3233d722a9Sopenharmony_ci            repr,
3333d722a9Sopenharmony_ci            values: BTreeSet::new(),
3433d722a9Sopenharmony_ci            previous: None,
3533d722a9Sopenharmony_ci        }
3633d722a9Sopenharmony_ci    }
3733d722a9Sopenharmony_ci
3833d722a9Sopenharmony_ci    pub fn insert(&mut self, expr: &Expr) -> Result<Discriminant> {
3933d722a9Sopenharmony_ci        let (discriminant, repr) = expr_to_discriminant(expr)?;
4033d722a9Sopenharmony_ci        match (self.repr, repr) {
4133d722a9Sopenharmony_ci            (None, Some(new_repr)) => {
4233d722a9Sopenharmony_ci                if let Some(limits) = Limits::of(new_repr) {
4333d722a9Sopenharmony_ci                    for &past in &self.values {
4433d722a9Sopenharmony_ci                        if limits.min <= past && past <= limits.max {
4533d722a9Sopenharmony_ci                            continue;
4633d722a9Sopenharmony_ci                        }
4733d722a9Sopenharmony_ci                        let msg = format!(
4833d722a9Sopenharmony_ci                            "discriminant value `{}` is outside the limits of {}",
4933d722a9Sopenharmony_ci                            past, new_repr,
5033d722a9Sopenharmony_ci                        );
5133d722a9Sopenharmony_ci                        return Err(Error::new(Span::call_site(), msg));
5233d722a9Sopenharmony_ci                    }
5333d722a9Sopenharmony_ci                }
5433d722a9Sopenharmony_ci                self.repr = Some(new_repr);
5533d722a9Sopenharmony_ci            }
5633d722a9Sopenharmony_ci            (Some(prev), Some(repr)) if prev != repr => {
5733d722a9Sopenharmony_ci                let msg = format!("expected {}, found {}", prev, repr);
5833d722a9Sopenharmony_ci                return Err(Error::new(Span::call_site(), msg));
5933d722a9Sopenharmony_ci            }
6033d722a9Sopenharmony_ci            _ => {}
6133d722a9Sopenharmony_ci        }
6233d722a9Sopenharmony_ci        insert(self, discriminant)
6333d722a9Sopenharmony_ci    }
6433d722a9Sopenharmony_ci
6533d722a9Sopenharmony_ci    pub fn insert_next(&mut self) -> Result<Discriminant> {
6633d722a9Sopenharmony_ci        let discriminant = match self.previous {
6733d722a9Sopenharmony_ci            None => Discriminant::zero(),
6833d722a9Sopenharmony_ci            Some(mut discriminant) => match discriminant.sign {
6933d722a9Sopenharmony_ci                Sign::Negative => {
7033d722a9Sopenharmony_ci                    discriminant.magnitude -= 1;
7133d722a9Sopenharmony_ci                    if discriminant.magnitude == 0 {
7233d722a9Sopenharmony_ci                        discriminant.sign = Sign::Positive;
7333d722a9Sopenharmony_ci                    }
7433d722a9Sopenharmony_ci                    discriminant
7533d722a9Sopenharmony_ci                }
7633d722a9Sopenharmony_ci                Sign::Positive => {
7733d722a9Sopenharmony_ci                    if discriminant.magnitude == u64::MAX {
7833d722a9Sopenharmony_ci                        let msg = format!("discriminant overflow on value after {}", u64::MAX);
7933d722a9Sopenharmony_ci                        return Err(Error::new(Span::call_site(), msg));
8033d722a9Sopenharmony_ci                    }
8133d722a9Sopenharmony_ci                    discriminant.magnitude += 1;
8233d722a9Sopenharmony_ci                    discriminant
8333d722a9Sopenharmony_ci                }
8433d722a9Sopenharmony_ci            },
8533d722a9Sopenharmony_ci        };
8633d722a9Sopenharmony_ci        insert(self, discriminant)
8733d722a9Sopenharmony_ci    }
8833d722a9Sopenharmony_ci
8933d722a9Sopenharmony_ci    pub fn inferred_repr(&self) -> Result<Atom> {
9033d722a9Sopenharmony_ci        if let Some(repr) = self.repr {
9133d722a9Sopenharmony_ci            return Ok(repr);
9233d722a9Sopenharmony_ci        }
9333d722a9Sopenharmony_ci        if self.values.is_empty() {
9433d722a9Sopenharmony_ci            return Ok(U8);
9533d722a9Sopenharmony_ci        }
9633d722a9Sopenharmony_ci        let min = *self.values.iter().next().unwrap();
9733d722a9Sopenharmony_ci        let max = *self.values.iter().next_back().unwrap();
9833d722a9Sopenharmony_ci        for limits in &LIMITS {
9933d722a9Sopenharmony_ci            if limits.min <= min && max <= limits.max {
10033d722a9Sopenharmony_ci                return Ok(limits.repr);
10133d722a9Sopenharmony_ci            }
10233d722a9Sopenharmony_ci        }
10333d722a9Sopenharmony_ci        let msg = "these discriminant values do not fit in any supported enum repr type";
10433d722a9Sopenharmony_ci        Err(Error::new(Span::call_site(), msg))
10533d722a9Sopenharmony_ci    }
10633d722a9Sopenharmony_ci}
10733d722a9Sopenharmony_ci
10833d722a9Sopenharmony_cifn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option<Atom>)> {
10933d722a9Sopenharmony_ci    match expr {
11033d722a9Sopenharmony_ci        Expr::Lit(expr) => {
11133d722a9Sopenharmony_ci            if let Lit::Int(lit) = &expr.lit {
11233d722a9Sopenharmony_ci                let discriminant = lit.base10_parse::<Discriminant>()?;
11333d722a9Sopenharmony_ci                let repr = parse_int_suffix(lit.suffix())?;
11433d722a9Sopenharmony_ci                return Ok((discriminant, repr));
11533d722a9Sopenharmony_ci            }
11633d722a9Sopenharmony_ci        }
11733d722a9Sopenharmony_ci        Expr::Unary(unary) => {
11833d722a9Sopenharmony_ci            if let UnOp::Neg(_) = unary.op {
11933d722a9Sopenharmony_ci                let (mut discriminant, repr) = expr_to_discriminant(&unary.expr)?;
12033d722a9Sopenharmony_ci                discriminant.sign = match discriminant.sign {
12133d722a9Sopenharmony_ci                    Sign::Positive => Sign::Negative,
12233d722a9Sopenharmony_ci                    Sign::Negative => Sign::Positive,
12333d722a9Sopenharmony_ci                };
12433d722a9Sopenharmony_ci                return Ok((discriminant, repr));
12533d722a9Sopenharmony_ci            }
12633d722a9Sopenharmony_ci        }
12733d722a9Sopenharmony_ci        _ => {}
12833d722a9Sopenharmony_ci    }
12933d722a9Sopenharmony_ci    Err(Error::new_spanned(
13033d722a9Sopenharmony_ci        expr,
13133d722a9Sopenharmony_ci        "enums with non-integer literal discriminants are not supported yet",
13233d722a9Sopenharmony_ci    ))
13333d722a9Sopenharmony_ci}
13433d722a9Sopenharmony_ci
13533d722a9Sopenharmony_cifn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result<Discriminant> {
13633d722a9Sopenharmony_ci    if let Some(expected_repr) = set.repr {
13733d722a9Sopenharmony_ci        if let Some(limits) = Limits::of(expected_repr) {
13833d722a9Sopenharmony_ci            if discriminant < limits.min || limits.max < discriminant {
13933d722a9Sopenharmony_ci                let msg = format!(
14033d722a9Sopenharmony_ci                    "discriminant value `{}` is outside the limits of {}",
14133d722a9Sopenharmony_ci                    discriminant, expected_repr,
14233d722a9Sopenharmony_ci                );
14333d722a9Sopenharmony_ci                return Err(Error::new(Span::call_site(), msg));
14433d722a9Sopenharmony_ci            }
14533d722a9Sopenharmony_ci        }
14633d722a9Sopenharmony_ci    }
14733d722a9Sopenharmony_ci    set.values.insert(discriminant);
14833d722a9Sopenharmony_ci    set.previous = Some(discriminant);
14933d722a9Sopenharmony_ci    Ok(discriminant)
15033d722a9Sopenharmony_ci}
15133d722a9Sopenharmony_ci
15233d722a9Sopenharmony_ciimpl Discriminant {
15333d722a9Sopenharmony_ci    pub const fn zero() -> Self {
15433d722a9Sopenharmony_ci        Discriminant {
15533d722a9Sopenharmony_ci            sign: Sign::Positive,
15633d722a9Sopenharmony_ci            magnitude: 0,
15733d722a9Sopenharmony_ci        }
15833d722a9Sopenharmony_ci    }
15933d722a9Sopenharmony_ci
16033d722a9Sopenharmony_ci    const fn pos(u: u64) -> Self {
16133d722a9Sopenharmony_ci        Discriminant {
16233d722a9Sopenharmony_ci            sign: Sign::Positive,
16333d722a9Sopenharmony_ci            magnitude: u,
16433d722a9Sopenharmony_ci        }
16533d722a9Sopenharmony_ci    }
16633d722a9Sopenharmony_ci
16733d722a9Sopenharmony_ci    const fn neg(i: i64) -> Self {
16833d722a9Sopenharmony_ci        Discriminant {
16933d722a9Sopenharmony_ci            sign: if i < 0 {
17033d722a9Sopenharmony_ci                Sign::Negative
17133d722a9Sopenharmony_ci            } else {
17233d722a9Sopenharmony_ci                Sign::Positive
17333d722a9Sopenharmony_ci            },
17433d722a9Sopenharmony_ci            // This is `i.abs() as u64` but without overflow on MIN. Uses the
17533d722a9Sopenharmony_ci            // fact that MIN.wrapping_abs() wraps back to MIN whose binary
17633d722a9Sopenharmony_ci            // representation is 1<<63, and thus the `as u64` conversion
17733d722a9Sopenharmony_ci            // produces 1<<63 too which happens to be the correct unsigned
17833d722a9Sopenharmony_ci            // magnitude.
17933d722a9Sopenharmony_ci            magnitude: i.wrapping_abs() as u64,
18033d722a9Sopenharmony_ci        }
18133d722a9Sopenharmony_ci    }
18233d722a9Sopenharmony_ci
18333d722a9Sopenharmony_ci    #[cfg(feature = "experimental-enum-variants-from-header")]
18433d722a9Sopenharmony_ci    pub const fn checked_succ(self) -> Option<Self> {
18533d722a9Sopenharmony_ci        match self.sign {
18633d722a9Sopenharmony_ci            Sign::Negative => {
18733d722a9Sopenharmony_ci                if self.magnitude == 1 {
18833d722a9Sopenharmony_ci                    Some(Discriminant::zero())
18933d722a9Sopenharmony_ci                } else {
19033d722a9Sopenharmony_ci                    Some(Discriminant {
19133d722a9Sopenharmony_ci                        sign: Sign::Negative,
19233d722a9Sopenharmony_ci                        magnitude: self.magnitude - 1,
19333d722a9Sopenharmony_ci                    })
19433d722a9Sopenharmony_ci                }
19533d722a9Sopenharmony_ci            }
19633d722a9Sopenharmony_ci            Sign::Positive => match self.magnitude.checked_add(1) {
19733d722a9Sopenharmony_ci                Some(magnitude) => Some(Discriminant {
19833d722a9Sopenharmony_ci                    sign: Sign::Positive,
19933d722a9Sopenharmony_ci                    magnitude,
20033d722a9Sopenharmony_ci                }),
20133d722a9Sopenharmony_ci                None => None,
20233d722a9Sopenharmony_ci            },
20333d722a9Sopenharmony_ci        }
20433d722a9Sopenharmony_ci    }
20533d722a9Sopenharmony_ci}
20633d722a9Sopenharmony_ci
20733d722a9Sopenharmony_ciimpl Display for Discriminant {
20833d722a9Sopenharmony_ci    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20933d722a9Sopenharmony_ci        if self.sign == Sign::Negative {
21033d722a9Sopenharmony_ci            f.write_str("-")?;
21133d722a9Sopenharmony_ci        }
21233d722a9Sopenharmony_ci        write!(f, "{}", self.magnitude)
21333d722a9Sopenharmony_ci    }
21433d722a9Sopenharmony_ci}
21533d722a9Sopenharmony_ci
21633d722a9Sopenharmony_ciimpl ToTokens for Discriminant {
21733d722a9Sopenharmony_ci    fn to_tokens(&self, tokens: &mut TokenStream) {
21833d722a9Sopenharmony_ci        if self.sign == Sign::Negative {
21933d722a9Sopenharmony_ci            Token![-](Span::call_site()).to_tokens(tokens);
22033d722a9Sopenharmony_ci        }
22133d722a9Sopenharmony_ci        Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens);
22233d722a9Sopenharmony_ci    }
22333d722a9Sopenharmony_ci}
22433d722a9Sopenharmony_ci
22533d722a9Sopenharmony_ciimpl FromStr for Discriminant {
22633d722a9Sopenharmony_ci    type Err = Error;
22733d722a9Sopenharmony_ci
22833d722a9Sopenharmony_ci    fn from_str(mut s: &str) -> Result<Self> {
22933d722a9Sopenharmony_ci        let sign = if s.starts_with('-') {
23033d722a9Sopenharmony_ci            s = &s[1..];
23133d722a9Sopenharmony_ci            Sign::Negative
23233d722a9Sopenharmony_ci        } else {
23333d722a9Sopenharmony_ci            Sign::Positive
23433d722a9Sopenharmony_ci        };
23533d722a9Sopenharmony_ci        match s.parse::<u64>() {
23633d722a9Sopenharmony_ci            Ok(magnitude) => Ok(Discriminant { sign, magnitude }),
23733d722a9Sopenharmony_ci            Err(_) => Err(Error::new(
23833d722a9Sopenharmony_ci                Span::call_site(),
23933d722a9Sopenharmony_ci                "discriminant value outside of supported range",
24033d722a9Sopenharmony_ci            )),
24133d722a9Sopenharmony_ci        }
24233d722a9Sopenharmony_ci    }
24333d722a9Sopenharmony_ci}
24433d722a9Sopenharmony_ci
24533d722a9Sopenharmony_ciimpl Ord for Discriminant {
24633d722a9Sopenharmony_ci    fn cmp(&self, other: &Self) -> Ordering {
24733d722a9Sopenharmony_ci        use self::Sign::{Negative, Positive};
24833d722a9Sopenharmony_ci        match (self.sign, other.sign) {
24933d722a9Sopenharmony_ci            (Negative, Negative) => self.magnitude.cmp(&other.magnitude).reverse(),
25033d722a9Sopenharmony_ci            (Negative, Positive) => Ordering::Less, // negative < positive
25133d722a9Sopenharmony_ci            (Positive, Negative) => Ordering::Greater, // positive > negative
25233d722a9Sopenharmony_ci            (Positive, Positive) => self.magnitude.cmp(&other.magnitude),
25333d722a9Sopenharmony_ci        }
25433d722a9Sopenharmony_ci    }
25533d722a9Sopenharmony_ci}
25633d722a9Sopenharmony_ci
25733d722a9Sopenharmony_ciimpl PartialOrd for Discriminant {
25833d722a9Sopenharmony_ci    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
25933d722a9Sopenharmony_ci        Some(self.cmp(other))
26033d722a9Sopenharmony_ci    }
26133d722a9Sopenharmony_ci}
26233d722a9Sopenharmony_ci
26333d722a9Sopenharmony_cifn parse_int_suffix(suffix: &str) -> Result<Option<Atom>> {
26433d722a9Sopenharmony_ci    if suffix.is_empty() {
26533d722a9Sopenharmony_ci        return Ok(None);
26633d722a9Sopenharmony_ci    }
26733d722a9Sopenharmony_ci    if let Some(atom) = Atom::from_str(suffix) {
26833d722a9Sopenharmony_ci        match atom {
26933d722a9Sopenharmony_ci            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize => return Ok(Some(atom)),
27033d722a9Sopenharmony_ci            _ => {}
27133d722a9Sopenharmony_ci        }
27233d722a9Sopenharmony_ci    }
27333d722a9Sopenharmony_ci    let msg = format!("unrecognized integer suffix: `{}`", suffix);
27433d722a9Sopenharmony_ci    Err(Error::new(Span::call_site(), msg))
27533d722a9Sopenharmony_ci}
27633d722a9Sopenharmony_ci
27733d722a9Sopenharmony_ci#[derive(Copy, Clone)]
27833d722a9Sopenharmony_cistruct Limits {
27933d722a9Sopenharmony_ci    repr: Atom,
28033d722a9Sopenharmony_ci    min: Discriminant,
28133d722a9Sopenharmony_ci    max: Discriminant,
28233d722a9Sopenharmony_ci}
28333d722a9Sopenharmony_ci
28433d722a9Sopenharmony_ciimpl Limits {
28533d722a9Sopenharmony_ci    fn of(repr: Atom) -> Option<Limits> {
28633d722a9Sopenharmony_ci        for limits in &LIMITS {
28733d722a9Sopenharmony_ci            if limits.repr == repr {
28833d722a9Sopenharmony_ci                return Some(*limits);
28933d722a9Sopenharmony_ci            }
29033d722a9Sopenharmony_ci        }
29133d722a9Sopenharmony_ci        None
29233d722a9Sopenharmony_ci    }
29333d722a9Sopenharmony_ci}
29433d722a9Sopenharmony_ci
29533d722a9Sopenharmony_ciconst LIMITS: [Limits; 8] = [
29633d722a9Sopenharmony_ci    Limits {
29733d722a9Sopenharmony_ci        repr: U8,
29833d722a9Sopenharmony_ci        min: Discriminant::zero(),
29933d722a9Sopenharmony_ci        max: Discriminant::pos(std::u8::MAX as u64),
30033d722a9Sopenharmony_ci    },
30133d722a9Sopenharmony_ci    Limits {
30233d722a9Sopenharmony_ci        repr: I8,
30333d722a9Sopenharmony_ci        min: Discriminant::neg(std::i8::MIN as i64),
30433d722a9Sopenharmony_ci        max: Discriminant::pos(std::i8::MAX as u64),
30533d722a9Sopenharmony_ci    },
30633d722a9Sopenharmony_ci    Limits {
30733d722a9Sopenharmony_ci        repr: U16,
30833d722a9Sopenharmony_ci        min: Discriminant::zero(),
30933d722a9Sopenharmony_ci        max: Discriminant::pos(std::u16::MAX as u64),
31033d722a9Sopenharmony_ci    },
31133d722a9Sopenharmony_ci    Limits {
31233d722a9Sopenharmony_ci        repr: I16,
31333d722a9Sopenharmony_ci        min: Discriminant::neg(std::i16::MIN as i64),
31433d722a9Sopenharmony_ci        max: Discriminant::pos(std::i16::MAX as u64),
31533d722a9Sopenharmony_ci    },
31633d722a9Sopenharmony_ci    Limits {
31733d722a9Sopenharmony_ci        repr: U32,
31833d722a9Sopenharmony_ci        min: Discriminant::zero(),
31933d722a9Sopenharmony_ci        max: Discriminant::pos(std::u32::MAX as u64),
32033d722a9Sopenharmony_ci    },
32133d722a9Sopenharmony_ci    Limits {
32233d722a9Sopenharmony_ci        repr: I32,
32333d722a9Sopenharmony_ci        min: Discriminant::neg(std::i32::MIN as i64),
32433d722a9Sopenharmony_ci        max: Discriminant::pos(std::i32::MAX as u64),
32533d722a9Sopenharmony_ci    },
32633d722a9Sopenharmony_ci    Limits {
32733d722a9Sopenharmony_ci        repr: U64,
32833d722a9Sopenharmony_ci        min: Discriminant::zero(),
32933d722a9Sopenharmony_ci        max: Discriminant::pos(std::u64::MAX),
33033d722a9Sopenharmony_ci    },
33133d722a9Sopenharmony_ci    Limits {
33233d722a9Sopenharmony_ci        repr: I64,
33333d722a9Sopenharmony_ci        min: Discriminant::neg(std::i64::MIN),
33433d722a9Sopenharmony_ci        max: Discriminant::pos(std::i64::MAX as u64),
33533d722a9Sopenharmony_ci    },
33633d722a9Sopenharmony_ci];
337