@@ -12,7 +12,10 @@ const RECURSION_LIMIT: usize = 64;
1212pub fn eval_array_len ( gcx : Gcx < ' _ > , size : & hir:: Expr < ' _ > ) -> Result < U256 , ErrorGuaranteed > {
1313 match ConstantEvaluator :: new ( gcx) . eval ( size) {
1414 Ok ( int) => {
15- if int. data . is_zero ( ) {
15+ if int. is_negative ( ) {
16+ let msg = "array length cannot be negative" ;
17+ Err ( gcx. dcx ( ) . err ( msg) . span ( size. span ) . emit ( ) )
18+ } else if int. data . is_zero ( ) {
1619 let msg = "array length must be greater than zero" ;
1720 Err ( gcx. dcx ( ) . err ( msg) . span ( size. span ) . emit ( ) )
1821 } else {
@@ -83,7 +86,7 @@ impl<'gcx> ConstantEvaluator<'gcx> {
8386 hir:: ExprKind :: Binary ( l, bin_op, r) => {
8487 let l = self . try_eval ( l) ?;
8588 let r = self . try_eval ( r) ?;
86- l. binop ( & r, bin_op. kind ) . map_err ( Into :: into)
89+ l. binop ( r, bin_op. kind ) . map_err ( Into :: into)
8790 }
8891 // hir::ExprKind::Call(_, _) => unimplemented!(),
8992 // hir::ExprKind::CallOptions(_, _) => unimplemented!(),
@@ -135,18 +138,50 @@ impl<'gcx> ConstantEvaluator<'gcx> {
135138 }
136139}
137140
141+ /// Represents an integer value with an explicit sign for literal type tracking.
142+ ///
143+ /// The `data` field always stores the absolute value of the number.
144+ /// The `negative` field indicates whether the value is negative.
138145pub struct IntScalar {
146+ /// The absolute value of the integer.
139147 pub data : U256 ,
148+ /// Whether the value is negative.
149+ pub negative : bool ,
140150}
141151
142152impl IntScalar {
153+ /// Creates a new non-negative integer value.
143154 pub fn new ( data : U256 ) -> Self {
144- Self { data }
155+ Self { data, negative : false }
156+ }
157+
158+ /// Creates a new integer value with the given sign.
159+ pub fn new_signed ( data : U256 , negative : bool ) -> Self {
160+ // Zero is never negative.
161+ Self { data, negative : negative && !data. is_zero ( ) }
162+ }
163+
164+ /// Returns the bit length of the integer value.
165+ ///
166+ /// This is the number of bits needed to represent the value,
167+ /// not including the sign bit.
168+ pub fn bit_len ( & self ) -> u64 {
169+ self . data . bit_len ( ) as u64
170+ }
171+
172+ /// Returns whether the value is negative.
173+ pub fn is_negative ( & self ) -> bool {
174+ self . negative
175+ }
176+
177+ /// Returns the negation of this value.
178+ pub fn negate ( self ) -> Self {
179+ if self . data . is_zero ( ) { self } else { Self :: new_signed ( self . data , !self . negative ) }
145180 }
146181
147182 /// Creates a new integer value from a boolean.
148183 pub fn from_bool ( value : bool ) -> Self {
149- Self { data : U256 :: from ( value as u8 ) }
184+ Self :: new ( U256 :: from ( value as u8 ) )
150185 }
151186
152187 /// Creates a new integer value from big-endian bytes.
@@ -155,7 +190,7 @@ impl IntScalar {
155190 ///
156191 /// Panics if `bytes` is empty or has a length greater than 32.
157192 pub fn from_be_bytes ( bytes : & [ u8 ] ) -> Self {
158- Self { data : U256 :: from_be_slice ( bytes) }
193+ Self :: new ( U256 :: from_be_slice ( bytes) )
159194 }
160195
161196 /// Converts the integer value to a boolean.
@@ -164,64 +199,109 @@ impl IntScalar {
164199 }
165200
166201 /// Applies the given unary operation to this value.
167- pub fn unop ( & self , op : hir:: UnOpKind ) -> Result < Self , EE > {
202+ pub fn unop ( self , op : hir:: UnOpKind ) -> Result < Self , EE > {
168203 Ok ( match op {
169204 hir:: UnOpKind :: PreInc
170205 | hir:: UnOpKind :: PreDec
171206 | hir:: UnOpKind :: PostInc
172207 | hir:: UnOpKind :: PostDec => return Err ( EE :: UnsupportedUnaryOp ) ,
173208 hir:: UnOpKind :: Not | hir:: UnOpKind :: BitNot => Self :: new ( !self . data ) ,
174- hir:: UnOpKind :: Neg => Self :: new ( self . data . wrapping_neg ( ) ) ,
209+ hir:: UnOpKind :: Neg => self . negate ( ) ,
175210 } )
176211 }
177212
178213 /// Applies the given binary operation to this value.
179- pub fn binop ( & self , r : & Self , op : hir:: BinOpKind ) -> Result < Self , EE > {
180- let l = self ;
214+ ///
215+ /// For signed arithmetic, this handles the sign tracking properly.
216+ pub fn binop ( self , r : Self , op : hir:: BinOpKind ) -> Result < Self , EE > {
217+ use hir:: BinOpKind :: * ;
181218 Ok ( match op {
182- // hir::BinOpKind::Lt => Self::from_bool(l.data < r.data),
183- // hir::BinOpKind::Le => Self::from_bool(l.data <= r.data),
184- // hir::BinOpKind::Gt => Self::from_bool(l.data > r.data),
185- // hir::BinOpKind::Ge => Self::from_bool(l.data >= r.data),
186- // hir::BinOpKind::Eq => Self::from_bool(l.data == r.data),
187- // hir::BinOpKind::Ne => Self::from_bool(l.data != r.data),
188- // hir::BinOpKind::Or => Self::from_bool(l.data != 0 || r.data != 0),
189- // hir::BinOpKind::And => Self::from_bool(l.data != 0 && r.data != 0),
190- hir:: BinOpKind :: BitOr => Self :: new ( l. data | r. data ) ,
191- hir:: BinOpKind :: BitAnd => Self :: new ( l. data & r. data ) ,
192- hir:: BinOpKind :: BitXor => Self :: new ( l. data ^ r. data ) ,
193- hir:: BinOpKind :: Shr => {
194- Self :: new ( l. data . wrapping_shr ( r. data . try_into ( ) . unwrap_or ( usize:: MAX ) ) )
195- }
196- hir:: BinOpKind :: Shl => {
197- Self :: new ( l. data . wrapping_shl ( r. data . try_into ( ) . unwrap_or ( usize:: MAX ) ) )
198- }
199- hir:: BinOpKind :: Sar => {
200- Self :: new ( l. data . arithmetic_shr ( r. data . try_into ( ) . unwrap_or ( usize:: MAX ) ) )
201- }
202- hir:: BinOpKind :: Add => {
203- Self :: new ( l. data . checked_add ( r. data ) . ok_or ( EE :: ArithmeticOverflow ) ?)
204- }
205- hir:: BinOpKind :: Sub => {
206- Self :: new ( l. data . checked_sub ( r. data ) . ok_or ( EE :: ArithmeticOverflow ) ?)
207- }
208- hir:: BinOpKind :: Pow => {
209- Self :: new ( l. data . checked_pow ( r. data ) . ok_or ( EE :: ArithmeticOverflow ) ?)
219+ Add => self . checked_add ( r) . ok_or ( EE :: ArithmeticOverflow ) ?,
220+ Sub => self . checked_sub ( r) . ok_or ( EE :: ArithmeticOverflow ) ?,
221+ Mul => self . checked_mul ( r) . ok_or ( EE :: ArithmeticOverflow ) ?,
222+ Div => self . checked_div ( r) . ok_or ( EE :: DivisionByZero ) ?,
223+ Rem => self . checked_rem ( r) . ok_or ( EE :: DivisionByZero ) ?,
224+ Pow => self . checked_pow ( r) . ok_or ( EE :: ArithmeticOverflow ) ?,
225+ BitOr => Self :: new ( self . data | r. data ) ,
226+ BitAnd => Self :: new ( self . data & r. data ) ,
227+ BitXor => Self :: new ( self . data ^ r. data ) ,
228+ Shr => Self :: new ( self . data . wrapping_shr ( r. data . try_into ( ) . unwrap_or ( usize:: MAX ) ) ) ,
229+ Shl => Self :: new ( self . data . wrapping_shl ( r. data . try_into ( ) . unwrap_or ( usize:: MAX ) ) ) ,
230+ Sar => Self :: new ( self . data . arithmetic_shr ( r. data . try_into ( ) . unwrap_or ( usize:: MAX ) ) ) ,
231+ Lt | Le | Gt | Ge | Eq | Ne | Or | And => return Err ( EE :: UnsupportedBinaryOp ) ,
232+ } )
233+ }
234+
235+ /// Checked addition with sign handling.
236+ fn checked_add ( self , r : Self ) -> Option < Self > {
237+ match ( self . negative , r. negative ) {
238+ // Both non-negative: simple add
239+ ( false , false ) => Some ( Self :: new ( self . data . checked_add ( r. data ) ?) ) ,
240+ // Both negative: negate(|a| + |b|)
241+ ( true , true ) => Some ( Self :: new_signed ( self . data . checked_add ( r. data ) ?, true ) ) ,
242+ // Different signs: subtract the smaller absolute value from the larger
243+ ( false , true ) => {
244+ // a + (-b) = a - b
245+ if self . data >= r. data {
246+ Some ( Self :: new ( self . data . checked_sub ( r. data ) ?) )
247+ } else {
248+ Some ( Self :: new_signed ( r. data . checked_sub ( self . data ) ?, true ) )
249+ }
210250 }
211- hir:: BinOpKind :: Mul => {
212- Self :: new ( l. data . checked_mul ( r. data ) . ok_or ( EE :: ArithmeticOverflow ) ?)
251+ ( true , false ) => {
252+ // (-a) + b = b - a
253+ if r. data >= self . data {
254+ Some ( Self :: new ( r. data . checked_sub ( self . data ) ?) )
255+ } else {
256+ Some ( Self :: new_signed ( self . data . checked_sub ( r. data ) ?, true ) )
257+ }
213258 }
214- hir:: BinOpKind :: Div => Self :: new ( l. data . checked_div ( r. data ) . ok_or ( EE :: DivisionByZero ) ?) ,
215- hir:: BinOpKind :: Rem => Self :: new ( l. data . checked_rem ( r. data ) . ok_or ( EE :: DivisionByZero ) ?) ,
216- hir:: BinOpKind :: Lt
217- | hir:: BinOpKind :: Le
218- | hir:: BinOpKind :: Gt
219- | hir:: BinOpKind :: Ge
220- | hir:: BinOpKind :: Eq
221- | hir:: BinOpKind :: Ne
222- | hir:: BinOpKind :: Or
223- | hir:: BinOpKind :: And => return Err ( EE :: UnsupportedBinaryOp ) ,
224- } )
259+ }
260+ }
261+
262+ /// Checked subtraction with sign handling.
263+ fn checked_sub ( self , r : Self ) -> Option < Self > {
264+ // a - b = a + (-b)
265+ self . checked_add ( r. negate ( ) )
266+ }
267+
268+ /// Checked multiplication with sign handling.
269+ fn checked_mul ( self , r : Self ) -> Option < Self > {
270+ let result = self . data . checked_mul ( r. data ) ?;
271+ // Result is negative if exactly one operand is negative
272+ Some ( Self :: new_signed ( result, self . negative != r. negative ) )
273+ }
274+
275+ /// Checked division with sign handling.
276+ fn checked_div ( self , r : Self ) -> Option < Self > {
277+ if r. data . is_zero ( ) {
278+ return None ;
279+ }
280+ let result = self . data . checked_div ( r. data ) ?;
281+ // Result is negative if exactly one operand is negative
282+ Some ( Self :: new_signed ( result, self . negative != r. negative ) )
283+ }
284+
285+ /// Checked remainder with sign handling.
286+ fn checked_rem ( self , r : Self ) -> Option < Self > {
287+ if r. data . is_zero ( ) {
288+ return None ;
289+ }
290+ let result = self . data . checked_rem ( r. data ) ?;
291+ // Result has the sign of the dividend
292+ Some ( Self :: new_signed ( result, self . negative ) )
293+ }
294+
295+ /// Checked exponentiation.
296+ fn checked_pow ( self , r : Self ) -> Option < Self > {
297+ // Exponent must be non-negative
298+ if r. negative {
299+ return None ;
300+ }
301+ let result = self . data . checked_pow ( r. data ) ?;
302+ // Result is negative if base is negative and exponent is odd
303+ let result_negative = self . negative && r. data . bit ( 0 ) ;
304+ Some ( Self :: new_signed ( result, result_negative) )
225305 }
226306}
227307
0 commit comments