1use crate::{builtins::Builtin, hir, ty::Gcx};
2use alloy_primitives::{B256, U256, keccak256};
3use num_bigint::{BigInt, BigUint, Sign};
4use num_traits::{One, Signed, Zero};
5use solar_ast::{LitKind, StrKind};
6use solar_interface::{ByteSymbol, Span, diagnostics::ErrorGuaranteed};
7use std::fmt;
8
9const RECURSION_LIMIT: usize = 64;
10const MAX_BITS: u64 = solar_ast::TypeSize::MAX as u64;
11
12pub fn erc7201_slot(namespace_id: &[u8]) -> B256 {
18 let inner = keccak256(namespace_id);
19 let inner = U256::from_be_bytes(inner.0).wrapping_sub(U256::from(1));
20 let mut outer = keccak256(inner.to_be_bytes::<32>());
21 outer.0[31] = 0;
22 outer
23}
24
25pub fn eval_array_len(gcx: Gcx<'_>, size: &hir::Expr<'_>) -> Result<U256, ErrorGuaranteed> {
27 match ConstantEvaluator::new(gcx).eval(size) {
28 Ok(int) => {
29 let Some(int) = int.as_u256() else {
30 let msg = "array length cannot be negative";
31 return Err(gcx.dcx().emit_err(size.span, msg));
32 };
33 if int.is_zero() {
34 let msg = "array length must be greater than zero";
35 Err(gcx.dcx().emit_err(size.span, msg))
36 } else {
37 Ok(int)
38 }
39 }
40 Err(guar) => Err(guar),
41 }
42}
43
44pub struct ConstantEvaluator<'gcx> {
50 pub gcx: Gcx<'gcx>,
51 depth: usize,
52}
53
54type EvalResult = Result<ConstValue, EvalError>;
55
56impl<'gcx> ConstantEvaluator<'gcx> {
57 pub fn new(gcx: Gcx<'gcx>) -> Self {
59 Self { gcx, depth: 0 }
60 }
61
62 pub fn eval(&mut self, expr: &hir::Expr<'_>) -> Result<IntScalar, ErrorGuaranteed> {
64 self.try_eval(expr).map_err(|err| self.emit_eval_error(expr, err))
65 }
66
67 pub fn try_eval(&mut self, expr: &hir::Expr<'_>) -> Result<IntScalar, EvalError> {
69 self.try_eval_value(expr).and_then(ConstValue::into_integer)
70 }
71
72 pub fn eval_value(&mut self, expr: &hir::Expr<'_>) -> Result<ConstValue, ErrorGuaranteed> {
75 self.try_eval_value(expr).map_err(|err| self.emit_eval_error(expr, err))
76 }
77
78 pub fn try_eval_value(&mut self, expr: &hir::Expr<'_>) -> EvalResult {
81 self.depth += 1;
82 if self.depth > RECURSION_LIMIT {
83 return Err(EE::RecursionLimitReached.spanned(expr.span));
84 }
85 let mut res = self.eval_expr(expr);
86 if let Err(e) = &mut res
87 && e.span.is_dummy()
88 {
89 e.span = expr.span;
90 }
91 self.depth = self.depth.checked_sub(1).unwrap();
92 res
93 }
94
95 pub fn emit_eval_error(&self, expr: &hir::Expr<'_>, err: EvalError) -> ErrorGuaranteed {
97 match err.kind {
98 EE::AlreadyEmitted(guar) => guar,
99 _ => {
100 let msg = format!("failed to evaluate constant: {}", err.kind.msg());
101 let label = "evaluation of constant value failed here";
102 self.gcx.dcx().emit_err_label(expr.span, msg, err.span, label)
103 }
104 }
105 }
106
107 fn eval_expr(&mut self, expr: &hir::Expr<'_>) -> EvalResult {
108 let expr = expr.peel_parens();
109 match expr.kind {
110 hir::ExprKind::Binary(l, bin_op, r) => {
113 let l = self.try_eval_value(l)?;
114 let r = self.try_eval_value(r)?;
115 l.binop(r, bin_op.kind).map_err(Into::into)
116 }
117 hir::ExprKind::Call(callee, ref args, opts) => self.eval_call(callee, args, opts),
118 hir::ExprKind::Ident(res) => {
120 let Some(v) = res.iter().find_map(|res| res.as_variable()) else {
122 return Err(EE::NonConstantVar.into());
123 };
124
125 let v = self.gcx.hir.variable(v);
126 if v.mutability != Some(hir::VarMut::Constant) {
127 return Err(EE::NonConstantVar.into());
128 }
129 self.try_eval_value(v.initializer.expect("constant variable has no initializer"))
130 }
131 hir::ExprKind::Lit(lit) => self.eval_lit(lit),
134 hir::ExprKind::Ternary(cond, t, f) => {
138 let ConstValue::Bool(cond) = self.try_eval_value(cond)? else {
139 return Err(EE::UnsupportedExpr.into());
140 };
141 if cond { self.try_eval_value(t) } else { self.try_eval_value(f) }
142 }
143 hir::ExprKind::Unary(un_op, v) => {
147 let v = self.try_eval_value(v)?;
148 v.unop(un_op.kind).map_err(Into::into)
149 }
150 hir::ExprKind::Err(guar) => Err(EE::AlreadyEmitted(guar).into()),
151 _ => Err(EE::UnsupportedExpr.into()),
152 }
153 }
154
155 fn eval_call(
156 &mut self,
157 callee: &hir::Expr<'_>,
158 args: &hir::CallArgs<'_>,
159 opts: Option<&hir::CallOptions<'_>>,
160 ) -> EvalResult {
161 if opts.is_none()
162 && let hir::ExprKind::Ident(res) = callee.peel_parens().kind
163 && matches!(res.first(), Some(hir::Res::Builtin(Builtin::Erc7201)))
164 && let hir::CallArgsKind::Unnamed([arg]) = args.kind
165 && let ConstValue::String(namespace_id) = self.try_eval_value(arg)?
166 {
167 return Ok(ConstValue::Integer(IntScalar::new(
168 erc7201_slot(namespace_id.as_byte_str()).into(),
169 )));
170 }
171 Err(EE::UnsupportedExpr.into())
172 }
173
174 fn eval_lit(&mut self, lit: &hir::Lit<'_>) -> EvalResult {
175 match lit.kind {
176 LitKind::Str(StrKind::Str | StrKind::Unicode, s, _) => Ok(ConstValue::String(s)),
177 LitKind::Str(StrKind::Hex, _, _) => Err(EE::UnsupportedLiteral.into()),
178 LitKind::Number(n) => Ok(ConstValue::Integer(IntScalar::new(n))),
179 LitKind::Address(address) => {
181 Ok(ConstValue::Integer(IntScalar::from_be_bytes(address.as_slice())))
182 }
183 LitKind::Bool(bool) => Ok(ConstValue::Bool(bool)),
184 LitKind::Err(guar) => Err(EE::AlreadyEmitted(guar).into()),
185 _ => Err(EE::UnsupportedLiteral.into()),
186 }
187 }
188}
189
190#[derive(Debug)]
192pub enum ConstValue {
193 Integer(IntScalar),
195 Bool(bool),
197 String(ByteSymbol),
199}
200
201impl ConstValue {
202 pub fn into_integer(self) -> Result<IntScalar, EvalError> {
204 match self {
205 Self::Integer(value) => Ok(value),
206 Self::Bool(_) => Err(EE::UnsupportedExpr.into()),
207 Self::String(_) => Err(EE::UnsupportedLiteral.into()),
208 }
209 }
210
211 pub fn unop(self, op: hir::UnOpKind) -> Result<Self, EE> {
213 Ok(match (self, op) {
214 (Self::Integer(value), op) => Self::Integer(value.unop(op)?),
215 (Self::Bool(value), hir::UnOpKind::Not) => Self::Bool(!value),
216 (Self::Bool(_) | Self::String(_), _) => return Err(EE::UnsupportedUnaryOp),
217 })
218 }
219
220 pub fn binop(self, rhs: Self, op: hir::BinOpKind) -> Result<Self, EE> {
222 use hir::BinOpKind::*;
223 Ok(match (self, rhs) {
224 (Self::Integer(lhs), Self::Integer(rhs)) => match op {
225 Lt => Self::Bool(lhs.data < rhs.data),
226 Le => Self::Bool(lhs.data <= rhs.data),
227 Gt => Self::Bool(lhs.data > rhs.data),
228 Ge => Self::Bool(lhs.data >= rhs.data),
229 Eq => Self::Bool(lhs.data == rhs.data),
230 Ne => Self::Bool(lhs.data != rhs.data),
231 Add | Sub | Mul | Div | Rem | Pow | BitOr | BitAnd | BitXor | Shr | Shl | Sar => {
232 Self::Integer(lhs.binop(rhs, op)?)
233 }
234 Or | And => return Err(EE::UnsupportedBinaryOp),
235 },
236 (Self::Bool(lhs), Self::Bool(rhs)) => match op {
237 And => Self::Bool(lhs && rhs),
238 Or => Self::Bool(lhs || rhs),
239 Eq => Self::Bool(lhs == rhs),
240 Ne => Self::Bool(lhs != rhs),
241 BitAnd => Self::Bool(lhs & rhs),
242 BitOr => Self::Bool(lhs | rhs),
243 BitXor => Self::Bool(lhs ^ rhs),
244 _ => return Err(EE::UnsupportedBinaryOp),
245 },
246 _ => return Err(EE::UnsupportedBinaryOp),
247 })
248 }
249}
250
251#[derive(Debug)]
253pub struct IntScalar {
254 data: BigInt,
255}
256
257impl IntScalar {
258 pub fn new(data: U256) -> Self {
260 Self { data: Self::bigint_from_u256(data) }
261 }
262
263 pub fn from_bool(value: bool) -> Self {
265 Self::new(U256::from(value as u8))
266 }
267
268 pub fn from_be_bytes(bytes: &[u8]) -> Self {
274 Self::new(U256::from_be_slice(bytes))
275 }
276
277 pub fn bit_len(&self) -> u64 {
281 Self::bits(&self.data)
282 }
283
284 pub fn is_negative(&self) -> bool {
286 self.data.is_negative()
287 }
288
289 pub fn is_signed(&self) -> bool {
291 self.is_negative()
292 }
293
294 pub fn is_zero(&self) -> bool {
296 self.data.is_zero()
297 }
298
299 pub fn as_u256(&self) -> Option<U256> {
301 let data = self.data.to_biguint()?;
302 U256::try_from_le_slice(&data.to_bytes_le())
303 }
304
305 pub fn as_evm_word(&self) -> U256 {
307 if let Some(value) = self.as_u256() {
308 return value;
309 }
310 let magnitude = U256::try_from_le_slice(&self.data.magnitude().to_bytes_le())
311 .expect("constant evaluator keeps integers within 256 bits");
312 U256::ZERO.wrapping_sub(magnitude)
313 }
314
315 pub fn to_bool(&self) -> bool {
317 !self.data.is_zero()
318 }
319
320 fn bigint_from_u256(data: U256) -> BigInt {
321 BigInt::from_bytes_be(Sign::Plus, &data.to_be_bytes::<32>())
322 }
323
324 fn checked(data: BigInt) -> Result<Self, EE> {
325 if Self::bits(&data) > MAX_BITS {
326 return Err(EE::ArithmeticOverflow);
327 }
328 Ok(Self { data })
329 }
330
331 fn bits(data: &BigInt) -> u64 {
332 if data.is_zero() {
333 return 1;
334 }
335 if data.is_positive() {
336 return data.bits();
337 }
338 let abs = data.magnitude();
339 if Self::is_power_of_two(abs) { abs.bits() } else { abs.bits() + 1 }
342 }
343
344 fn is_power_of_two(value: &BigUint) -> bool {
345 !value.is_zero() && (value & (value - BigUint::one())).is_zero()
346 }
347
348 fn negate(self) -> Result<Self, EE> {
349 Self::checked(-self.data)
350 }
351
352 fn shift_amount(r: Self) -> Option<usize> {
353 r.as_u256()?.try_into().ok()
354 }
355
356 fn bitop(self, r: Self, f: impl FnOnce(BigInt, BigInt) -> BigInt) -> Result<Self, EE> {
357 Self::checked(f(self.data, r.data))
358 }
359
360 pub fn unop(self, op: hir::UnOpKind) -> Result<Self, EE> {
362 Ok(match op {
363 hir::UnOpKind::PreInc
364 | hir::UnOpKind::PreDec
365 | hir::UnOpKind::PostInc
366 | hir::UnOpKind::PostDec => return Err(EE::UnsupportedUnaryOp),
367 hir::UnOpKind::Not | hir::UnOpKind::BitNot => Self::checked(!self.data)?,
368 hir::UnOpKind::Neg => self.negate()?,
369 })
370 }
371
372 pub fn binop(self, r: Self, op: hir::BinOpKind) -> Result<Self, EE> {
376 use hir::BinOpKind::*;
377 Ok(match op {
378 Add => Self::checked(self.data + r.data)?,
379 Sub => Self::checked(self.data - r.data)?,
380 Mul => Self::checked(self.data * r.data)?,
381 Div => {
382 if r.data.is_zero() {
383 return Err(EE::DivisionByZero);
384 }
385 Self::checked(self.data / r.data)?
386 }
387 Rem => {
388 if r.data.is_zero() {
389 return Err(EE::DivisionByZero);
390 }
391 Self::checked(self.data % r.data)?
392 }
393 Pow => {
394 if r.is_negative() {
395 return Err(EE::ArithmeticOverflow);
396 }
397 self.checked_pow(r)?
398 }
399 BitOr => self.bitop(r, |a, b| a | b)?,
400 BitAnd => self.bitop(r, |a, b| a & b)?,
401 BitXor => self.bitop(r, |a, b| a ^ b)?,
402 Shr => {
403 let r = Self::shift_amount(r).ok_or(EE::ArithmeticOverflow)?;
404 Self::checked(self.data >> r)?
405 }
406 Shl => self.checked_shl(r)?,
407 Sar => return Err(EE::UnsupportedBinaryOp),
408 Lt | Le | Gt | Ge | Eq | Ne | Or | And => return Err(EE::UnsupportedBinaryOp),
409 })
410 }
411
412 fn checked_shl(self, r: Self) -> Result<Self, EE> {
413 if self.data.is_zero() {
414 return Ok(self);
415 }
416 let shift: u64 = r
417 .as_u256()
418 .ok_or(EE::ArithmeticOverflow)?
419 .try_into()
420 .map_err(|_| EE::ArithmeticOverflow)?;
421 let bits = Self::bits(&self.data);
422 if shift > MAX_BITS.saturating_sub(bits) {
423 return Err(EE::ArithmeticOverflow);
424 }
425 Self::checked(self.data << usize::try_from(shift).map_err(|_| EE::ArithmeticOverflow)?)
426 }
427
428 fn checked_pow(self, r: Self) -> Result<Self, EE> {
429 if self.data.is_zero() {
430 return Ok(self);
431 }
432 if self.data.is_one() {
433 return Ok(self);
434 }
435 if self.data == BigInt::from(-1) {
436 let exp = r.as_u256().ok_or(EE::ArithmeticOverflow)?;
437 let is_odd = exp.bit(0);
438 return Self::checked(if is_odd { self.data } else { BigInt::one() });
439 }
440 let exp = r.as_u256().ok_or(EE::ArithmeticOverflow)?;
441 if exp > U256::from(MAX_BITS) {
442 return Err(EE::ArithmeticOverflow);
443 }
444 let exp = exp.try_into().map_err(|_| EE::ArithmeticOverflow)?;
445 Self::checked(self.data.pow(exp))
446 }
447}
448
449#[derive(Debug)]
450pub enum EvalErrorKind {
451 RecursionLimitReached,
452 ArithmeticOverflow,
453 DivisionByZero,
454 UnsupportedLiteral,
455 UnsupportedUnaryOp,
456 UnsupportedBinaryOp,
457 UnsupportedExpr,
458 NonConstantVar,
459 AlreadyEmitted(ErrorGuaranteed),
460}
461use EvalErrorKind as EE;
462
463impl EvalErrorKind {
464 pub fn spanned(self, span: Span) -> EvalError {
465 EvalError { kind: self, span }
466 }
467
468 fn msg(&self) -> &'static str {
469 match self {
470 Self::RecursionLimitReached => "recursion limit reached",
471 Self::ArithmeticOverflow => "arithmetic overflow",
472 Self::DivisionByZero => "attempted to divide by zero",
473 Self::UnsupportedLiteral => "unsupported literal",
474 Self::UnsupportedUnaryOp => "unsupported unary operation",
475 Self::UnsupportedBinaryOp => "unsupported binary operation",
476 Self::UnsupportedExpr => "unsupported expression",
477 Self::NonConstantVar => "only constant variables are allowed",
478 Self::AlreadyEmitted(_) => unreachable!(),
479 }
480 }
481}
482
483#[derive(Debug)]
484pub struct EvalError {
485 pub span: Span,
486 pub kind: EvalErrorKind,
487}
488
489impl From<EE> for EvalError {
490 fn from(value: EE) -> Self {
491 Self { kind: value, span: Span::DUMMY }
492 }
493}
494
495impl fmt::Display for EvalError {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 self.kind.msg().fmt(f)
498 }
499}
500
501impl std::error::Error for EvalError {}
502
503#[cfg(test)]
504mod tests {
505 use super::erc7201_slot;
506 use alloy_primitives::b256;
507
508 #[test]
509 fn erc7201_slot_matches_eip_example() {
510 assert_eq!(
511 erc7201_slot(b"example.main"),
512 b256!("183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500")
513 );
514 }
515
516 #[test]
517 fn erc7201_slot_subtracts_from_full_inner_hash() {
518 assert_eq!(
519 erc7201_slot(b"85"),
520 b256!("06d0d983459328e82eacb1bf2d6fadfa38a6896e9d4cbfe0e1aa41c6281bab00")
521 );
522 }
523}