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).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