solang/sema/
eval.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use super::{
4    ast::{Diagnostic, Expression, Namespace, Type},
5    diagnostics::Diagnostics,
6};
7use num_bigint::BigInt;
8use num_bigint::Sign;
9use num_rational::BigRational;
10use num_traits::One;
11use num_traits::ToPrimitive;
12use num_traits::Zero;
13use solang_parser::pt;
14use solang_parser::pt::{CodeLocation, Loc};
15use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Shl, Shr, Sub};
16
17/// Resolve an expression where a compile-time constant is expected
18pub fn eval_const_number(
19    expr: &Expression,
20    ns: &Namespace,
21    diagnostics: &mut Diagnostics,
22) -> Result<(pt::Loc, BigInt), ()> {
23    match expr {
24        Expression::Add {
25            loc, left, right, ..
26        } => Ok((
27            *loc,
28            eval_const_number(left, ns, diagnostics)?.1
29                + eval_const_number(right, ns, diagnostics)?.1,
30        )),
31        Expression::Subtract {
32            loc, left, right, ..
33        } => Ok((
34            *loc,
35            eval_const_number(left, ns, diagnostics)?.1
36                - eval_const_number(right, ns, diagnostics)?.1,
37        )),
38        Expression::Multiply {
39            loc, left, right, ..
40        } => Ok((
41            *loc,
42            eval_const_number(left, ns, diagnostics)?.1
43                * eval_const_number(right, ns, diagnostics)?.1,
44        )),
45        Expression::Divide {
46            loc, left, right, ..
47        } => {
48            let divisor = eval_const_number(right, ns, diagnostics)?.1;
49
50            if divisor.is_zero() {
51                diagnostics.push(Diagnostic::error(*loc, "divide by zero".to_string()));
52
53                Err(())
54            } else {
55                Ok((*loc, eval_const_number(left, ns, diagnostics)?.1 / divisor))
56            }
57        }
58        Expression::Modulo {
59            loc, left, right, ..
60        } => {
61            let divisor = eval_const_number(right, ns, diagnostics)?.1;
62
63            if divisor.is_zero() {
64                diagnostics.push(Diagnostic::error(*loc, "divide by zero".to_string()));
65
66                Err(())
67            } else {
68                Ok((*loc, eval_const_number(left, ns, diagnostics)?.1 % divisor))
69            }
70        }
71        Expression::BitwiseAnd {
72            loc, left, right, ..
73        } => Ok((
74            *loc,
75            eval_const_number(left, ns, diagnostics)?.1
76                & eval_const_number(right, ns, diagnostics)?.1,
77        )),
78        Expression::BitwiseOr {
79            loc, left, right, ..
80        } => Ok((
81            *loc,
82            eval_const_number(left, ns, diagnostics)?.1
83                | eval_const_number(right, ns, diagnostics)?.1,
84        )),
85        Expression::BitwiseXor {
86            loc, left, right, ..
87        } => Ok((
88            *loc,
89            eval_const_number(left, ns, diagnostics)?.1
90                ^ eval_const_number(right, ns, diagnostics)?.1,
91        )),
92        Expression::Power { loc, base, exp, .. } => {
93            let b = eval_const_number(base, ns, diagnostics)?.1;
94            let mut e = eval_const_number(exp, ns, diagnostics)?.1;
95
96            if e.sign() == Sign::Minus {
97                diagnostics.push(Diagnostic::error(
98                    *loc,
99                    "power cannot take negative number as exponent".to_string(),
100                ));
101
102                Err(())
103            } else if e.sign() == Sign::NoSign {
104                Ok((*loc, BigInt::one()))
105            } else {
106                let mut res = b.clone();
107                e -= BigInt::one();
108                while e.sign() == Sign::Plus {
109                    res *= b.clone();
110                    e -= BigInt::one();
111                }
112                Ok((*loc, res))
113            }
114        }
115        Expression::ShiftLeft {
116            loc, left, right, ..
117        } => {
118            let l = eval_const_number(left, ns, diagnostics)?.1;
119            let r = eval_const_number(right, ns, diagnostics)?.1;
120            let r = match r.to_usize() {
121                Some(r) => r,
122                None => {
123                    diagnostics.push(Diagnostic::error(*loc, format!("cannot left shift by {r}")));
124
125                    return Err(());
126                }
127            };
128            Ok((*loc, l << r))
129        }
130        Expression::ShiftRight {
131            loc, left, right, ..
132        } => {
133            let l = eval_const_number(left, ns, diagnostics)?.1;
134            let r = eval_const_number(right, ns, diagnostics)?.1;
135            let r = match r.to_usize() {
136                Some(r) => r,
137                None => {
138                    diagnostics.push(Diagnostic::error(*loc, format!("right left shift by {r}")));
139
140                    return Err(());
141                }
142            };
143            Ok((*loc, l >> r))
144        }
145        Expression::NumberLiteral { loc, value, .. } => Ok((*loc, value.clone())),
146        Expression::ZeroExt { loc, expr, .. } => {
147            Ok((*loc, eval_const_number(expr, ns, diagnostics)?.1))
148        }
149        Expression::SignExt { loc, expr, .. } => {
150            Ok((*loc, eval_const_number(expr, ns, diagnostics)?.1))
151        }
152        Expression::Cast { loc, expr, .. } => {
153            Ok((*loc, eval_const_number(expr, ns, diagnostics)?.1))
154        }
155        Expression::Not { loc, expr: n } => Ok((*loc, !eval_const_number(n, ns, diagnostics)?.1)),
156        Expression::BitwiseNot { loc, expr, .. } => {
157            Ok((*loc, !eval_const_number(expr, ns, diagnostics)?.1))
158        }
159        Expression::Negate { loc, expr, .. } => {
160            Ok((*loc, -eval_const_number(expr, ns, diagnostics)?.1))
161        }
162        Expression::ConstantVariable {
163            contract_no: Some(contract_no),
164            var_no,
165            ..
166        } => {
167            let var = &ns.contracts[*contract_no].variables[*var_no];
168
169            if let Some(init) = &var.initializer {
170                eval_const_number(init, ns, diagnostics)
171            } else {
172                // we should have errored about this already
173                Err(())
174            }
175        }
176        Expression::ConstantVariable {
177            contract_no: None,
178            var_no,
179            ..
180        } => {
181            let var = &ns.constants[*var_no];
182
183            if let Some(init) = &var.initializer {
184                eval_const_number(init, ns, diagnostics)
185            } else {
186                // we should have errored about this already
187                Err(())
188            }
189        }
190        _ => {
191            diagnostics.push(Diagnostic::error(
192                expr.loc(),
193                "expression not allowed in constant number expression".to_string(),
194            ));
195
196            Err(())
197        }
198    }
199}
200
201/// Resolve an expression where a compile-time constant(rational) is expected
202pub fn eval_const_rational(
203    expr: &Expression,
204    ns: &Namespace,
205) -> Result<(pt::Loc, BigRational), Diagnostic> {
206    match expr {
207        Expression::Add {
208            loc, left, right, ..
209        } => Ok((
210            *loc,
211            eval_const_rational(left, ns)?.1 + eval_const_rational(right, ns)?.1,
212        )),
213        Expression::Subtract {
214            loc, left, right, ..
215        } => Ok((
216            *loc,
217            eval_const_rational(left, ns)?.1 - eval_const_rational(right, ns)?.1,
218        )),
219        Expression::Multiply {
220            loc,
221            left: l,
222            right: r,
223            ..
224        } => Ok((
225            *loc,
226            eval_const_rational(l, ns)?.1 * eval_const_rational(r, ns)?.1,
227        )),
228        Expression::Divide {
229            loc, left, right, ..
230        } => {
231            let divisor = eval_const_rational(right, ns)?.1;
232
233            if divisor.is_zero() {
234                Err(Diagnostic::error(*loc, "divide by zero".to_string()))
235            } else {
236                Ok((*loc, eval_const_rational(left, ns)?.1 / divisor))
237            }
238        }
239        Expression::Modulo {
240            loc,
241            left: l,
242            right: r,
243            ..
244        } => {
245            let divisor = eval_const_rational(r, ns)?.1;
246
247            if divisor.is_zero() {
248                Err(Diagnostic::error(*loc, "divide by zero".to_string()))
249            } else {
250                Ok((*loc, eval_const_rational(l, ns)?.1 % divisor))
251            }
252        }
253        Expression::NumberLiteral { loc, value, .. } => {
254            Ok((*loc, BigRational::from_integer(value.clone())))
255        }
256        Expression::RationalNumberLiteral { loc, value, .. } => Ok((*loc, value.clone())),
257        Expression::Cast { loc, expr, .. } => Ok((*loc, eval_const_rational(expr, ns)?.1)),
258        Expression::Negate { loc, expr, .. } => Ok((*loc, -eval_const_rational(expr, ns)?.1)),
259        Expression::ConstantVariable {
260            contract_no: Some(contract_no),
261            var_no,
262            ..
263        } => {
264            let expr = ns.contracts[*contract_no].variables[*var_no]
265                .initializer
266                .as_ref()
267                .unwrap()
268                .clone();
269
270            eval_const_rational(&expr, ns)
271        }
272        Expression::ConstantVariable {
273            contract_no: None,
274            var_no,
275            ..
276        } => {
277            let expr = ns.constants[*var_no].initializer.as_ref().unwrap().clone();
278
279            eval_const_rational(&expr, ns)
280        }
281        _ => Err(Diagnostic::error(
282            expr.loc(),
283            "expression not allowed in constant rational number expression".to_string(),
284        )),
285    }
286}
287
288/// Function that recurses the expression and folds number literals by calling 'eval_constants_in_expression'.
289/// If the expression is an arithmetic operation of two number literals, overflow_check() will be called on the result.
290pub(super) fn check_term_for_constant_overflow(expr: &Expression, ns: &mut Namespace) -> bool {
291    match expr {
292        Expression::Add { .. }
293        | Expression::Subtract { .. }
294        | Expression::Multiply { .. }
295        | Expression::Divide { .. }
296        | Expression::Modulo { .. }
297        | Expression::Power { .. }
298        | Expression::ShiftLeft { .. }
299        | Expression::ShiftRight { .. }
300        | Expression::BitwiseAnd { .. }
301        | Expression::BitwiseOr { .. }
302        | Expression::BitwiseXor { .. }
303        | Expression::NumberLiteral { .. } => {
304            match eval_constants_in_expression(expr, &mut ns.diagnostics) {
305                (
306                    Some(Expression::NumberLiteral {
307                        loc,
308                        ty,
309                        value: result,
310                    }),
311                    _,
312                ) => {
313                    if let Some(diagnostic) = overflow_diagnostic(&result, &ty, &loc) {
314                        ns.diagnostics.push(diagnostic);
315                    }
316
317                    return false;
318                }
319                (None, false) => {
320                    return false;
321                }
322                _ => {}
323            }
324        }
325        _ => {}
326    }
327
328    true
329}
330
331/// This function recursively folds number literals in a given expression.
332/// It returns an Option<Expression> which is the result of the folding if the operands are number literals, and a boolean flag that is set to false if the recursion should stop.
333pub(crate) fn eval_constants_in_expression(
334    expr: &Expression,
335    diagnostics: &mut Diagnostics,
336) -> (Option<Expression>, bool) {
337    match expr {
338        Expression::Add {
339            loc,
340            ty,
341            unchecked: _,
342            left,
343            right,
344        } => {
345            let left = eval_constants_in_expression(left, diagnostics).0;
346            let right = eval_constants_in_expression(right, diagnostics).0;
347
348            if let (
349                Some(Expression::NumberLiteral { value: left, .. }),
350                Some(Expression::NumberLiteral { value: right, .. }),
351            ) = (left, right)
352            {
353                (
354                    Some(Expression::NumberLiteral {
355                        loc: *loc,
356                        ty: ty.clone(),
357                        value: left.add(right),
358                    }),
359                    true,
360                )
361            } else {
362                (None, true)
363            }
364        }
365        Expression::Subtract {
366            loc,
367            ty,
368            unchecked: _,
369            left,
370            right,
371        } => {
372            let left = eval_constants_in_expression(left, diagnostics).0;
373            let right = eval_constants_in_expression(right, diagnostics).0;
374
375            if let (
376                Some(Expression::NumberLiteral { value: left, .. }),
377                Some(Expression::NumberLiteral { value: right, .. }),
378            ) = (&left, &right)
379            {
380                (
381                    Some(Expression::NumberLiteral {
382                        loc: *loc,
383                        ty: ty.clone(),
384                        value: left.sub(right),
385                    }),
386                    true,
387                )
388            } else {
389                (None, true)
390            }
391        }
392
393        Expression::Multiply {
394            loc,
395            ty,
396            unchecked: _,
397            left,
398            right,
399        } => {
400            let left = eval_constants_in_expression(left, diagnostics).0;
401            let right = eval_constants_in_expression(right, diagnostics).0;
402
403            if let (
404                Some(Expression::NumberLiteral { value: left, .. }),
405                Some(Expression::NumberLiteral { value: right, .. }),
406            ) = (&left, &right)
407            {
408                (
409                    Some(Expression::NumberLiteral {
410                        loc: *loc,
411                        ty: ty.clone(),
412                        value: left.mul(right),
413                    }),
414                    true,
415                )
416            } else {
417                (None, true)
418            }
419        }
420        Expression::Divide {
421            loc,
422            ty,
423            left,
424            right,
425        } => {
426            let left = eval_constants_in_expression(left, diagnostics).0;
427            let right = eval_constants_in_expression(right, diagnostics).0;
428
429            if let (
430                Some(Expression::NumberLiteral { value: left, .. }),
431                Some(Expression::NumberLiteral { value: right, .. }),
432            ) = (&left, &right)
433            {
434                if right.is_zero() {
435                    diagnostics.push(Diagnostic::error(*loc, "divide by zero".to_string()));
436                    (None, false)
437                } else {
438                    (
439                        Some(Expression::NumberLiteral {
440                            loc: *loc,
441                            ty: ty.clone(),
442                            value: left.div(right),
443                        }),
444                        true,
445                    )
446                }
447            } else {
448                (None, true)
449            }
450        }
451
452        Expression::Modulo {
453            loc,
454            ty,
455            left,
456            right,
457        } => {
458            let left = eval_constants_in_expression(left, diagnostics).0;
459            let right = eval_constants_in_expression(right, diagnostics).0;
460
461            if let (
462                Some(Expression::NumberLiteral { value: left, .. }),
463                Some(Expression::NumberLiteral { value: right, .. }),
464            ) = (&left, &right)
465            {
466                if right.is_zero() {
467                    diagnostics.push(Diagnostic::error(*loc, "divide by zero".to_string()));
468                    (None, false)
469                } else {
470                    (
471                        Some(Expression::NumberLiteral {
472                            loc: *loc,
473                            ty: ty.clone(),
474                            value: left % right,
475                        }),
476                        true,
477                    )
478                }
479            } else {
480                (None, true)
481            }
482        }
483        Expression::Power {
484            loc,
485            ty,
486            unchecked: _,
487            base,
488            exp,
489        } => {
490            let base = eval_constants_in_expression(base, diagnostics).0;
491            let exp = eval_constants_in_expression(exp, diagnostics).0;
492
493            if let (
494                Some(Expression::NumberLiteral { value: left, .. }),
495                Some(Expression::NumberLiteral {
496                    loc: right_loc,
497                    value: right,
498                    ..
499                }),
500            ) = (&base, &exp)
501            {
502                if overflow_diagnostic(right, &Type::Uint(16), right_loc).is_some() {
503                    diagnostics.push(Diagnostic::error(
504                        *right_loc,
505                        format!("power by {right} is not possible"),
506                    ));
507                    (None, false)
508                } else {
509                    (
510                        Some(Expression::NumberLiteral {
511                            loc: *loc,
512                            ty: ty.clone(),
513                            value: left.pow(right.to_u16().unwrap().into()),
514                        }),
515                        true,
516                    )
517                }
518            } else {
519                (None, true)
520            }
521        }
522        Expression::ShiftLeft {
523            loc,
524            ty,
525            left,
526            right,
527        } => {
528            let left = eval_constants_in_expression(left, diagnostics).0;
529            let right = eval_constants_in_expression(right, diagnostics).0;
530
531            if let (
532                Some(Expression::NumberLiteral { value: left, .. }),
533                Some(Expression::NumberLiteral {
534                    loc: right_loc,
535                    value: right,
536                    ..
537                }),
538            ) = (&left, &right)
539            {
540                if overflow_diagnostic(right, &Type::Uint(64), right_loc).is_some() {
541                    diagnostics.push(Diagnostic::error(
542                        *right_loc,
543                        format!("left shift by {right} is not possible"),
544                    ));
545                    (None, false)
546                } else {
547                    (
548                        Some(Expression::NumberLiteral {
549                            loc: *loc,
550                            ty: ty.clone(),
551                            value: left.shl(right.to_u64().unwrap()),
552                        }),
553                        true,
554                    )
555                }
556            } else {
557                (None, true)
558            }
559        }
560
561        Expression::ShiftRight {
562            loc,
563            ty,
564            left,
565            right,
566            sign: _,
567        } => {
568            let left = eval_constants_in_expression(left, diagnostics).0;
569            let right = eval_constants_in_expression(right, diagnostics).0;
570
571            if let (
572                Some(Expression::NumberLiteral { value: left, .. }),
573                Some(Expression::NumberLiteral {
574                    loc: right_loc,
575                    value: right,
576                    ..
577                }),
578            ) = (&left, &right)
579            {
580                if overflow_diagnostic(right, &Type::Uint(64), right_loc).is_some() {
581                    diagnostics.push(Diagnostic::error(
582                        *right_loc,
583                        format!("right shift by {right} is not possible"),
584                    ));
585                    (None, false)
586                } else {
587                    (
588                        Some(Expression::NumberLiteral {
589                            loc: *loc,
590                            ty: ty.clone(),
591                            value: left.shr(right.to_u64().unwrap()),
592                        }),
593                        true,
594                    )
595                }
596            } else {
597                (None, true)
598            }
599        }
600        Expression::BitwiseAnd {
601            loc,
602            ty,
603            left,
604            right,
605        } => {
606            let left = eval_constants_in_expression(left, diagnostics).0;
607            let right = eval_constants_in_expression(right, diagnostics).0;
608
609            if let (
610                Some(Expression::NumberLiteral { value: left, .. }),
611                Some(Expression::NumberLiteral { value: right, .. }),
612            ) = (&left, &right)
613            {
614                (
615                    Some(Expression::NumberLiteral {
616                        loc: *loc,
617                        ty: ty.clone(),
618                        value: left.bitand(right),
619                    }),
620                    true,
621                )
622            } else {
623                (None, true)
624            }
625        }
626        Expression::BitwiseOr {
627            loc,
628            ty,
629            left,
630            right,
631        } => {
632            let left = eval_constants_in_expression(left, diagnostics).0;
633            let right = eval_constants_in_expression(right, diagnostics).0;
634
635            if let (
636                Some(Expression::NumberLiteral { value: left, .. }),
637                Some(Expression::NumberLiteral { value: right, .. }),
638            ) = (&left, &right)
639            {
640                (
641                    Some(Expression::NumberLiteral {
642                        loc: *loc,
643                        ty: ty.clone(),
644                        value: left.bitor(right),
645                    }),
646                    true,
647                )
648            } else {
649                (None, true)
650            }
651        }
652        Expression::BitwiseXor {
653            loc,
654            ty,
655            left,
656            right,
657        } => {
658            let left = eval_constants_in_expression(left, diagnostics).0;
659            let right = eval_constants_in_expression(right, diagnostics).0;
660
661            if let (
662                Some(Expression::NumberLiteral { value: left, .. }),
663                Some(Expression::NumberLiteral { value: right, .. }),
664            ) = (&left, &right)
665            {
666                (
667                    Some(Expression::NumberLiteral {
668                        loc: *loc,
669                        ty: ty.clone(),
670                        value: left.bitxor(right),
671                    }),
672                    true,
673                )
674            } else {
675                (None, true)
676            }
677        }
678        Expression::ZeroExt { loc, to, expr } => {
679            let expr = eval_constants_in_expression(expr, diagnostics).0;
680            if let Some(Expression::NumberLiteral { value, .. }) = expr {
681                (
682                    Some(Expression::NumberLiteral {
683                        loc: *loc,
684                        ty: to.clone(),
685                        value,
686                    }),
687                    true,
688                )
689            } else {
690                (None, true)
691            }
692        }
693        Expression::SignExt { loc, to, expr } => {
694            let expr = eval_constants_in_expression(expr, diagnostics).0;
695            if let Some(Expression::NumberLiteral { value, .. }) = expr {
696                (
697                    Some(Expression::NumberLiteral {
698                        loc: *loc,
699                        ty: to.clone(),
700                        value,
701                    }),
702                    true,
703                )
704            } else {
705                (None, true)
706            }
707        }
708        Expression::NumberLiteral { .. } => (Some(expr.clone()), true),
709        _ => (None, true),
710    }
711}
712
713/// Function that takes a BigInt and an expected type. If the number of bits in the type required to represent the BigInt is not sufficient, it will return a diagnostic.
714pub(crate) fn overflow_diagnostic(result: &BigInt, ty: &Type, loc: &Loc) -> Option<Diagnostic> {
715    if result.bits() > 1024 {
716        // Do not try to print large values. For example:
717        // uint x = 80 ** 0x100000;
718        // is an enormous value, and having all those decimals in the diagnostic does not help. Also,
719        // printing all those decimals will take a long time
720        if let Type::Uint(bits) = ty {
721            // If the result sign is minus, throw an error.
722            if let Sign::Minus = result.sign() {
723                return Some(Diagnostic::error(
724                    *loc,
725                    format!( "large negative value does not fit into type uint{}. Cannot implicitly convert signed literal to unsigned type.",
726                    ty.get_type_size()),
727                ));
728            }
729
730            // If bits of the result is more than bits of the type, throw and error.
731            if result.bits() > *bits as u64 {
732                return Some(Diagnostic::error(
733                    *loc,
734                    format!(
735                        "value is too large to fit into type uint{}",
736                        ty.get_type_size(),
737                    ),
738                ));
739            }
740        }
741
742        if let Type::Int(bits) = ty {
743            // If number of bits is more than what the type can hold. BigInt.bits() is not used here since it disregards the sign.
744            if result.to_signed_bytes_be().len() * 8 > (*bits as usize) {
745                return Some(Diagnostic::error(
746                    *loc,
747                    format!(
748                        "value is too large to fit into type int{}",
749                        ty.get_type_size(),
750                    ),
751                ));
752            }
753        }
754    } else {
755        if let Type::Uint(bits) = ty {
756            // If the result sign is minus, throw an error.
757            if let Sign::Minus = result.sign() {
758                return Some(Diagnostic::error(
759                *loc,
760            format!( "negative value {} does not fit into type uint{}. Cannot implicitly convert signed literal to unsigned type.",result,ty.get_type_size()),
761            ));
762            }
763
764            // If bits of the result is more than bits of the type, throw and error.
765            if result.bits() > *bits as u64 {
766                return Some(Diagnostic::error(
767                    *loc,
768                    format!(
769                        "value {} does not fit into type uint{}.",
770                        result,
771                        ty.get_type_size(),
772                    ),
773                ));
774            }
775        }
776
777        if let Type::Int(bits) = ty {
778            // If number of bits is more than what the type can hold. BigInt.bits() is not used here since it disregards the sign.
779            if result.to_signed_bytes_be().len() * 8 > (*bits as usize) {
780                return Some(Diagnostic::error(
781                    *loc,
782                    format!(
783                        "value {} does not fit into type int{}.",
784                        result,
785                        ty.get_type_size(),
786                    ),
787                ));
788            }
789        }
790    }
791    None
792}