Răsfoiți Sursa

Ensure that "require(i < 2**255);" parses correctly (#1324)

Found with uniswap v3. This change also includes a little refactoring, and now Expression::NotEqual is generated by sema.

Before this change,

require(i < 2**255);

Gave the error: value 53919893334301279589334030174039261347274288845081144962207220498432 does not fit into type uint8.

This is because with ResolveTo::Unknown the integers default to the smallest size possible.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 2 ani în urmă
părinte
comite
e692c0c5a4

+ 44 - 16
src/codegen/constant_folding.rs

@@ -1345,27 +1345,55 @@ fn expression(
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);
 
-            (
-                Expression::Equal {
-                    loc: *loc,
-                    left: Box::new(left.0),
-                    right: Box::new(right.0),
-                },
-                false,
-            )
+            if let (
+                Expression::BytesLiteral { value: l, .. },
+                Expression::BytesLiteral { value: r, .. },
+            ) = (&left.0, &right.0)
+            {
+                (
+                    Expression::BoolLiteral {
+                        loc: *loc,
+                        value: l == r,
+                    },
+                    true,
+                )
+            } else {
+                (
+                    Expression::Equal {
+                        loc: *loc,
+                        left: Box::new(left.0),
+                        right: Box::new(right.0),
+                    },
+                    false,
+                )
+            }
         }
         Expression::NotEqual { loc, left, right } => {
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);
 
-            (
-                Expression::NotEqual {
-                    loc: *loc,
-                    left: Box::new(left.0),
-                    right: Box::new(right.0),
-                },
-                false,
-            )
+            if let (
+                Expression::BytesLiteral { value: l, .. },
+                Expression::BytesLiteral { value: r, .. },
+            ) = (&left.0, &right.0)
+            {
+                (
+                    Expression::BoolLiteral {
+                        loc: *loc,
+                        value: l != r,
+                    },
+                    true,
+                )
+            } else {
+                (
+                    Expression::NotEqual {
+                        loc: *loc,
+                        left: Box::new(left.0),
+                        right: Box::new(right.0),
+                    },
+                    false,
+                )
+            }
         }
         Expression::Not { loc, expr } => {
             let expr = expression(expr, vars, cfg, ns);

+ 34 - 5
src/emit/expression.rs

@@ -785,12 +785,41 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
             }
         }
         Expression::NotEqual { left, right, .. } => {
-            let left = expression(target, bin, left, vartab, function, ns).into_int_value();
-            let right = expression(target, bin, right, vartab, function, ns).into_int_value();
+            if left.ty().is_address() {
+                let mut res = bin.context.bool_type().const_int(0, false);
+                let left = expression(target, bin, left, vartab, function, ns).into_array_value();
+                let right = expression(target, bin, right, vartab, function, ns).into_array_value();
 
-            bin.builder
-                .build_int_compare(IntPredicate::NE, left, right, "")
-                .into()
+                // TODO: Address should be passed around as pointer. Once this is done, we can replace
+                // this with a call to address_equal()
+                for index in 0..ns.address_length {
+                    let l = bin
+                        .builder
+                        .build_extract_value(left, index as u32, "left")
+                        .unwrap()
+                        .into_int_value();
+                    let r = bin
+                        .builder
+                        .build_extract_value(right, index as u32, "right")
+                        .unwrap()
+                        .into_int_value();
+
+                    res = bin.builder.build_or(
+                        res,
+                        bin.builder.build_int_compare(IntPredicate::NE, l, r, ""),
+                        "cmp",
+                    );
+                }
+
+                res.into()
+            } else {
+                let left = expression(target, bin, left, vartab, function, ns).into_int_value();
+                let right = expression(target, bin, right, vartab, function, ns).into_int_value();
+
+                bin.builder
+                    .build_int_compare(IntPredicate::NE, left, right, "")
+                    .into()
+            }
         }
         Expression::More {
             signed,

+ 9 - 2
src/emit/strings.rs

@@ -566,9 +566,16 @@ pub(super) fn string_location<'a, T: TargetRuntime<'a> + ?Sized>(
                 .const_int(literal.len() as u64, false),
         ),
         StringLocation::RunTime(e) => {
-            let v = expression(target, bin, e, vartab, function, ns);
+            if let Expression::BytesLiteral { value, .. } = e.as_ref() {
+                (
+                    bin.emit_global_string("const_string", value, true),
+                    bin.context.i32_type().const_int(value.len() as u64, false),
+                )
+            } else {
+                let v = expression(target, bin, e, vartab, function, ns);
 
-            (bin.vector_bytes(v), bin.vector_len(v))
+                (bin.vector_bytes(v), bin.vector_len(v))
+            }
         }
     }
 }

+ 139 - 44
src/sema/expression/arithmetic.rs

@@ -3,7 +3,7 @@
 use crate::sema::ast::{Expression, Namespace, RetrieveType, StringLocation, Type};
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::eval::eval_const_rational;
-use crate::sema::expression::integers::{coerce, coerce_number, get_int_length};
+use crate::sema::expression::integers::{coerce, coerce_number, type_bits_and_sign};
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{user_defined_operator, ExprContext, ResolveTo};
 use crate::sema::symtable::Symtable;
@@ -223,8 +223,8 @@ pub(super) fn shift_left(
     check_var_usage_expression(ns, &left, &right, symtable);
     // left hand side may be bytes/int/uint
     // right hand size may be int/uint
-    let _ = get_int_length(&left.ty(), &l.loc(), true, ns, diagnostics)?;
-    let (right_length, _) = get_int_length(&right.ty(), &r.loc(), false, ns, diagnostics)?;
+    let _ = type_bits_and_sign(&left.ty(), &l.loc(), true, ns, diagnostics)?;
+    let (right_length, _) = type_bits_and_sign(&right.ty(), &r.loc(), false, ns, diagnostics)?;
 
     let left_type = left.ty().deref_any().clone();
 
@@ -254,8 +254,8 @@ pub(super) fn shift_right(
     let left_type = left.ty().deref_any().clone();
     // left hand side may be bytes/int/uint
     // right hand size may be int/uint
-    let _ = get_int_length(&left_type, &l.loc(), true, ns, diagnostics)?;
-    let (right_length, _) = get_int_length(&right.ty(), &r.loc(), false, ns, diagnostics)?;
+    let _ = type_bits_and_sign(&left_type, &l.loc(), true, ns, diagnostics)?;
+    let (right_length, _) = type_bits_and_sign(&right.ty(), &r.loc(), false, ns, diagnostics)?;
 
     Ok(Expression::ShiftRight {
         loc: *loc,
@@ -521,29 +521,147 @@ pub(super) fn power(
 /// Test for equality; first check string equality, then integer equality
 pub(super) fn equal(
     loc: &pt::Loc,
-    left: Expression,
-    right: Expression,
+    l: &pt::Expression,
+    r: &pt::Expression,
+    context: &ExprContext,
     ns: &mut Namespace,
+    symtable: &mut Symtable,
     diagnostics: &mut Diagnostics,
 ) -> Result<Expression, ()> {
-    // Comparing stringliteral against stringliteral
-    if let (Expression::BytesLiteral { value: l, .. }, Expression::BytesLiteral { value: r, .. }) =
-        (&left, &right)
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+
+    check_var_usage_expression(ns, &left, &right, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&left, &right],
+        pt::UserDefinedOperator::Equal,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
+    }
+
+    let left_type = left.ty();
+    let right_type = right.ty();
+
+    if let Some(expr) =
+        is_string_equal(loc, &left, &left_type, &right, &right_type, ns, diagnostics)?
     {
-        return Ok(Expression::BoolLiteral {
-            loc: *loc,
-            value: l == r,
-        });
+        return Ok(expr);
+    }
+
+    let ty = coerce(
+        &left_type,
+        &left.loc(),
+        &right_type,
+        &right.loc(),
+        ns,
+        diagnostics,
+    )?;
+
+    if ty.is_rational() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot use rational numbers with '==' operator".into(),
+        ));
+        return Err(());
+    }
+
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+
+    let expr = Expression::Equal {
+        loc: *loc,
+        left: Box::new(left.cast(&left.loc(), &ty, true, ns, diagnostics)?),
+        right: Box::new(right.cast(&right.loc(), &ty, true, ns, diagnostics)?),
+    };
+
+    Ok(expr)
+}
+
+pub(super) fn not_equal(
+    loc: &pt::Loc,
+    l: &pt::Expression,
+    r: &pt::Expression,
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+) -> Result<Expression, ()> {
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+
+    check_var_usage_expression(ns, &left, &right, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&left, &right],
+        pt::UserDefinedOperator::NotEqual,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
     }
 
     let left_type = left.ty();
     let right_type = right.ty();
 
+    if let Some(expr) =
+        is_string_equal(loc, &left, &left_type, &right, &right_type, ns, diagnostics)?
+    {
+        return Ok(Expression::Not {
+            loc: *loc,
+            expr: expr.into(),
+        });
+    }
+
+    let ty = coerce(
+        &left_type,
+        &left.loc(),
+        &right_type,
+        &right.loc(),
+        ns,
+        diagnostics,
+    )?;
+
+    if ty.is_rational() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot use rational numbers with '!=' operator".into(),
+        ));
+        return Err(());
+    }
+
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+
+    let expr = Expression::NotEqual {
+        loc: *loc,
+        left: Box::new(left.cast(&left.loc(), &ty, true, ns, diagnostics)?),
+        right: Box::new(right.cast(&right.loc(), &ty, true, ns, diagnostics)?),
+    };
+
+    Ok(expr)
+}
+
+/// If the left and right arguments are part of string comparison, return
+/// a string comparision expression, else None.
+fn is_string_equal(
+    loc: &pt::Loc,
+    left: &Expression,
+    left_type: &Type,
+    right: &Expression,
+    right_type: &Type,
+    ns: &Namespace,
+    diagnostics: &mut Diagnostics,
+) -> Result<Option<Expression>, ()> {
     // compare string against literal
     match (&left, &right_type.deref_any()) {
         (Expression::BytesLiteral { value: l, .. }, Type::String)
         | (Expression::BytesLiteral { value: l, .. }, Type::DynamicBytes) => {
-            return Ok(Expression::StringCompare {
+            return Ok(Some(Expression::StringCompare {
                 loc: *loc,
                 left: StringLocation::RunTime(Box::new(right.cast(
                     &right.loc(),
@@ -553,7 +671,7 @@ pub(super) fn equal(
                     diagnostics,
                 )?)),
                 right: StringLocation::CompileTime(l.clone()),
-            });
+            }));
         }
         _ => {}
     }
@@ -561,7 +679,7 @@ pub(super) fn equal(
     match (&right, &left_type.deref_any()) {
         (Expression::BytesLiteral { value, .. }, Type::String)
         | (Expression::BytesLiteral { value, .. }, Type::DynamicBytes) => {
-            return Ok(Expression::StringCompare {
+            return Ok(Some(Expression::StringCompare {
                 loc: *loc,
                 left: StringLocation::RunTime(Box::new(left.cast(
                     &left.loc(),
@@ -571,7 +689,7 @@ pub(super) fn equal(
                     diagnostics,
                 )?)),
                 right: StringLocation::CompileTime(value.clone()),
-            });
+            }));
         }
         _ => {}
     }
@@ -579,7 +697,7 @@ pub(super) fn equal(
     // compare string
     match (&left_type.deref_any(), &right_type.deref_any()) {
         (Type::String, Type::String) | (Type::DynamicBytes, Type::DynamicBytes) => {
-            return Ok(Expression::StringCompare {
+            return Ok(Some(Expression::StringCompare {
                 loc: *loc,
                 left: StringLocation::RunTime(Box::new(left.cast(
                     &left.loc(),
@@ -595,35 +713,12 @@ pub(super) fn equal(
                     ns,
                     diagnostics,
                 )?)),
-            });
+            }));
         }
         _ => {}
     }
 
-    let ty = coerce(
-        &left_type,
-        &left.loc(),
-        &right_type,
-        &right.loc(),
-        ns,
-        diagnostics,
-    )?;
-
-    let expr = Expression::Equal {
-        loc: *loc,
-        left: Box::new(left.cast(&left.loc(), &ty, true, ns, diagnostics)?),
-        right: Box::new(right.cast(&right.loc(), &ty, true, ns, diagnostics)?),
-    };
-
-    if ty.is_rational() {
-        diagnostics.push(Diagnostic::error(
-            *loc,
-            "cannot use rational numbers with '!=' or '==' operator".into(),
-        ));
-        return Err(());
-    }
-
-    Ok(expr)
+    Ok(None)
 }
 
 /// Try string concatenation

+ 4 - 3
src/sema/expression/assign.rs

@@ -3,7 +3,7 @@
 use crate::sema::ast::{Expression, Namespace, RetrieveType, Type};
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::eval::check_term_for_constant_overflow;
-use crate::sema::expression::integers::get_int_length;
+use crate::sema::expression::integers::type_bits_and_sign;
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{ExprContext, ResolveTo};
 use crate::sema::symtable::Symtable;
@@ -198,8 +198,9 @@ pub(super) fn assign_expr(
      -> Result<Expression, ()> {
         let set = match expr {
             pt::Expression::AssignShiftLeft(..) | pt::Expression::AssignShiftRight(..) => {
-                let left_length = get_int_length(ty, loc, true, ns, diagnostics)?;
-                let right_length = get_int_length(&set_type, &left.loc(), false, ns, diagnostics)?;
+                let left_length = type_bits_and_sign(ty, loc, true, ns, diagnostics)?;
+                let right_length =
+                    type_bits_and_sign(&set_type, &left.loc(), false, ns, diagnostics)?;
 
                 // TODO: does shifting by negative value need compiletime/runtime check?
                 if left_length == right_length {

+ 15 - 12
src/sema/expression/integers.rs

@@ -4,6 +4,7 @@ use crate::sema::ast::{Expression, Namespace, Type};
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::expression::ResolveTo;
 use num_bigint::{BigInt, Sign};
+use num_traits::Zero;
 use solang_parser::diagnostics::Diagnostic;
 use solang_parser::pt;
 
@@ -40,7 +41,9 @@ pub(super) fn coerce(
     coerce_number(l, l_loc, r, r_loc, true, false, ns, diagnostics)
 }
 
-pub(super) fn get_int_length(
+/// Calculate the number of bits and the sign of a type, or generate a diagnostic
+/// that the type that is not allowed.
+pub(super) fn type_bits_and_sign(
     l: &Type,
     l_loc: &pt::Loc,
     allow_bytes: bool,
@@ -74,8 +77,8 @@ pub(super) fn get_int_length(
             ));
             Err(())
         }
-        Type::Ref(n) => get_int_length(n, l_loc, allow_bytes, ns, diagnostics),
-        Type::StorageRef(_, n) => get_int_length(n, l_loc, allow_bytes, ns, diagnostics),
+        Type::Ref(n) => type_bits_and_sign(n, l_loc, allow_bytes, ns, diagnostics),
+        Type::StorageRef(_, n) => type_bits_and_sign(n, l_loc, allow_bytes, ns, diagnostics),
         _ => {
             diagnostics.push(Diagnostic::error(
                 *l_loc,
@@ -153,9 +156,9 @@ pub fn coerce_number(
         _ => (),
     }
 
-    let (left_len, left_signed) = get_int_length(l, l_loc, false, ns, diagnostics)?;
+    let (left_len, left_signed) = type_bits_and_sign(l, l_loc, false, ns, diagnostics)?;
 
-    let (right_len, right_signed) = get_int_length(r, r_loc, false, ns, diagnostics)?;
+    let (right_len, right_signed) = type_bits_and_sign(r, r_loc, false, ns, diagnostics)?;
 
     Ok(match (left_signed, right_signed) {
         (true, true) => Type::Int(left_len.max(right_len)),
@@ -189,19 +192,19 @@ pub fn bigint_to_expression(
 
     if let ResolveTo::Type(resolve_to) = resolve_to {
         if *resolve_to != Type::Unresolved {
-            if !resolve_to.is_integer(ns) {
+            if !(resolve_to.is_integer(ns) || matches!(resolve_to, Type::Bytes(_)) && n.is_zero()) {
                 diagnostics.push(Diagnostic::cast_error(
                     *loc,
                     format!("expected '{}', found integer", resolve_to.to_string(ns)),
                 ));
                 return Err(());
-            } else {
-                return Ok(Expression::NumberLiteral {
-                    loc: *loc,
-                    ty: resolve_to.clone(),
-                    value: n.clone(),
-                });
             }
+
+            return Ok(Expression::NumberLiteral {
+                loc: *loc,
+                ty: resolve_to.clone(),
+                value: n.clone(),
+            });
         }
     }
 

+ 359 - 307
src/sema/expression/resolve_expression.rs

@@ -3,12 +3,12 @@
 use crate::sema::expression::{
     arithmetic::{
         addition, bitwise_and, bitwise_or, bitwise_xor, divide, equal, incr_decr, modulo, multiply,
-        power, shift_left, shift_right, subtract,
+        not_equal, power, shift_left, shift_right, subtract,
     },
     assign::{assign_expr, assign_single},
     constructor::{constructor_named_args, new},
     function_call::{call_expr, named_call_expr},
-    integers::{bigint_to_expression, coerce, coerce_number, get_int_length},
+    integers::{bigint_to_expression, coerce, coerce_number, type_bits_and_sign},
     literals::{
         address_literal, array_literal, hex_literal, hex_number_literal, number_literal,
         rational_number_literal, string_literal, unit_literal,
@@ -131,215 +131,22 @@ pub fn expression(
             power(loc, b, e, context, ns, symtable, diagnostics, resolve_to)
         }
         // compare
-        pt::Expression::More(loc, l, r) => {
-            let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-
-            check_var_usage_expression(ns, &left, &right, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&left, &right],
-                pt::UserDefinedOperator::More,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            let ty = coerce_number(
-                &left.ty(),
-                &l.loc(),
-                &right.ty(),
-                &r.loc(),
-                true,
-                true,
-                ns,
-                diagnostics,
-            )?;
-
-            let expr = Expression::More {
-                loc: *loc,
-                left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
-                right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            };
-
-            if ty.is_rational() {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot use rational numbers with '>' operator".into(),
-                ));
-                return Err(());
-            }
-
-            Ok(expr)
-        }
-        pt::Expression::Less(loc, l, r) => {
-            let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-
-            check_var_usage_expression(ns, &left, &right, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&left, &right],
-                pt::UserDefinedOperator::Less,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            let ty = coerce_number(
-                &left.ty(),
-                &l.loc(),
-                &right.ty(),
-                &r.loc(),
-                true,
-                true,
-                ns,
-                diagnostics,
-            )?;
-
-            let expr = Expression::Less {
-                loc: *loc,
-                left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
-                right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            };
-
-            if ty.is_rational() {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot use rational numbers with '<' operator".into(),
-                ));
-                return Err(());
-            }
-
-            Ok(expr)
+        pt::Expression::More(loc, left, right) => {
+            more(left, right, context, ns, symtable, diagnostics, loc)
         }
-        pt::Expression::MoreEqual(loc, l, r) => {
-            let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            check_var_usage_expression(ns, &left, &right, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&left, &right],
-                pt::UserDefinedOperator::MoreEqual,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            let ty = coerce_number(
-                &left.ty(),
-                &l.loc(),
-                &right.ty(),
-                &r.loc(),
-                true,
-                true,
-                ns,
-                diagnostics,
-            )?;
-
-            let expr = Expression::MoreEqual {
-                loc: *loc,
-                left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
-                right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            };
-
-            if ty.is_rational() {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot use rational numbers with '>=' operator".into(),
-                ));
-                return Err(());
-            }
-
-            Ok(expr)
+        pt::Expression::Less(loc, left, right) => {
+            less(left, right, context, ns, symtable, diagnostics, loc)
         }
-        pt::Expression::LessEqual(loc, l, r) => {
-            let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            check_var_usage_expression(ns, &left, &right, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&left, &right],
-                pt::UserDefinedOperator::LessEqual,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            let ty = coerce_number(
-                &left.ty(),
-                &l.loc(),
-                &right.ty(),
-                &r.loc(),
-                true,
-                true,
-                ns,
-                diagnostics,
-            )?;
-
-            let expr = Expression::LessEqual {
-                loc: *loc,
-                left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
-                right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            };
-
-            if ty.is_rational() {
-                diagnostics.push(Diagnostic::error(
-                    *loc,
-                    "cannot use rational numbers with '<=' operator".into(),
-                ));
-                return Err(());
-            }
-
-            Ok(expr)
+        pt::Expression::MoreEqual(loc, left, right) => {
+            more_equal(left, right, context, ns, symtable, diagnostics, loc)
         }
-        pt::Expression::Equal(loc, l, r) => {
-            let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-
-            check_var_usage_expression(ns, &left, &right, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&left, &right],
-                pt::UserDefinedOperator::Equal,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            equal(loc, left, right, ns, diagnostics)
+        pt::Expression::LessEqual(loc, left, right) => {
+            less_equal(left, right, context, ns, symtable, diagnostics, loc)
         }
+        pt::Expression::Equal(loc, l, r) => equal(loc, l, r, context, ns, symtable, diagnostics),
 
         pt::Expression::NotEqual(loc, l, r) => {
-            let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-            let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
-
-            check_var_usage_expression(ns, &left, &right, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&left, &right],
-                pt::UserDefinedOperator::NotEqual,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            Ok(Expression::Not {
-                loc: *loc,
-                expr: equal(loc, left, right, ns, diagnostics)?.into(),
-            })
+            not_equal(loc, l, r, context, ns, symtable, diagnostics)
         }
         // unary expressions
         pt::Expression::Not(loc, e) => {
@@ -352,114 +159,17 @@ pub fn expression(
             })
         }
         pt::Expression::BitwiseNot(loc, e) => {
-            let expr = expression(e, context, ns, symtable, diagnostics, resolve_to)?;
-
-            used_variable(ns, &expr, symtable);
-
-            if let Some(expr) = user_defined_operator(
-                loc,
-                &[&expr],
-                pt::UserDefinedOperator::BitwiseNot,
-                diagnostics,
-                ns,
-            ) {
-                return Ok(expr);
-            }
-
-            let expr_ty = expr.ty();
-
-            get_int_length(&expr_ty, loc, true, ns, diagnostics)?;
-
-            Ok(Expression::BitwiseNot {
-                loc: *loc,
-                ty: expr_ty,
-                expr: Box::new(expr),
-            })
+            bitwise_not(e, context, ns, symtable, diagnostics, resolve_to, loc)
+        }
+        pt::Expression::Negate(loc, e) => {
+            negate(e, loc, ns, diagnostics, resolve_to, context, symtable)
         }
-        pt::Expression::Negate(loc, e) => match e.as_ref() {
-            pt::Expression::NumberLiteral(_, integer, exp, unit) => {
-                let unit = unit_literal(loc, unit, ns, diagnostics);
-
-                number_literal(loc, integer, exp, ns, &-unit, diagnostics, resolve_to)
-            }
-            pt::Expression::HexNumberLiteral(_, v, unit) => {
-                if unit.is_some() {
-                    diagnostics.push(Diagnostic::error(
-                        *loc,
-                        "hexadecimal numbers cannot be used with unit denominations".into(),
-                    ));
-                }
-
-                // a hex literal with a minus before it cannot be an address literal or a bytesN value
-                let s: String = v.chars().skip(2).filter(|v| *v != '_').collect();
-
-                let n = BigInt::from_str_radix(&s, 16).unwrap();
-
-                bigint_to_expression(loc, &-n, ns, diagnostics, resolve_to, Some(s.len()))
-            }
-            pt::Expression::RationalNumberLiteral(loc, integer, fraction, exp, unit) => {
-                let unit = unit_literal(loc, unit, ns, diagnostics);
-
-                rational_number_literal(
-                    loc,
-                    integer,
-                    fraction,
-                    exp,
-                    &-unit,
-                    ns,
-                    diagnostics,
-                    resolve_to,
-                )
-            }
-            e => {
-                let expr = expression(e, context, ns, symtable, diagnostics, resolve_to)?;
-
-                used_variable(ns, &expr, symtable);
-
-                if let Some(expr) = user_defined_operator(
-                    loc,
-                    &[&expr],
-                    pt::UserDefinedOperator::Negate,
-                    diagnostics,
-                    ns,
-                ) {
-                    return Ok(expr);
-                }
-
-                let expr_type = expr.ty();
-
-                if let Expression::NumberLiteral { value, .. } = expr {
-                    bigint_to_expression(loc, &-value, ns, diagnostics, resolve_to, None)
-                } else if let Expression::RationalNumberLiteral { ty, value: r, .. } = expr {
-                    Ok(Expression::RationalNumberLiteral {
-                        loc: *loc,
-                        ty,
-                        value: -r,
-                    })
-                } else {
-                    get_int_length(&expr_type, loc, false, ns, diagnostics)?;
-
-                    if !expr_type.is_signed_int(ns) {
-                        diagnostics.push(Diagnostic::error(
-                            *loc,
-                            "negate not allowed on unsigned".to_string(),
-                        ));
-                    }
-
-                    Ok(Expression::Negate {
-                        loc: *loc,
-                        ty: expr_type,
-                        expr: Box::new(expr),
-                    })
-                }
-            }
-        },
         pt::Expression::UnaryPlus(loc, e) => {
             let expr = expression(e, context, ns, symtable, diagnostics, resolve_to)?;
             used_variable(ns, &expr, symtable);
             let expr_type = expr.ty();
 
-            get_int_length(&expr_type, loc, false, ns, diagnostics)?;
+            type_bits_and_sign(&expr_type, loc, false, ns, diagnostics)?;
 
             diagnostics.push(Diagnostic::error(
                 *loc,
@@ -722,3 +432,345 @@ pub fn expression(
         }
     }
 }
+
+fn bitwise_not(
+    expr: &pt::Expression,
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+    resolve_to: ResolveTo,
+    loc: &pt::Loc,
+) -> Result<Expression, ()> {
+    let expr = expression(expr, context, ns, symtable, diagnostics, resolve_to)?;
+
+    used_variable(ns, &expr, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&expr],
+        pt::UserDefinedOperator::BitwiseNot,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
+    }
+
+    let expr_ty = expr.ty();
+
+    // Ensure that the argument is an integer or fixed bytes type
+    type_bits_and_sign(&expr_ty, loc, true, ns, diagnostics)?;
+
+    Ok(Expression::BitwiseNot {
+        loc: *loc,
+        ty: expr_ty,
+        expr: Box::new(expr),
+    })
+}
+
+fn negate(
+    expr: &pt::Expression,
+    loc: &pt::Loc,
+    ns: &mut Namespace,
+    diagnostics: &mut Diagnostics,
+    resolve_to: ResolveTo,
+    context: &ExprContext,
+    symtable: &mut Symtable,
+) -> Result<Expression, ()> {
+    match expr {
+        pt::Expression::NumberLiteral(_, integer, exp, unit) => {
+            let unit = unit_literal(loc, unit, ns, diagnostics);
+
+            number_literal(loc, integer, exp, ns, &-unit, diagnostics, resolve_to)
+        }
+        pt::Expression::HexNumberLiteral(_, v, unit) => {
+            if unit.is_some() {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "hexadecimal numbers cannot be used with unit denominations".into(),
+                ));
+            }
+
+            // a hex literal with a minus before it cannot be an address literal or a bytesN value
+            let s: String = v.chars().skip(2).filter(|v| *v != '_').collect();
+
+            let n = BigInt::from_str_radix(&s, 16).unwrap();
+
+            bigint_to_expression(loc, &-n, ns, diagnostics, resolve_to, Some(s.len()))
+        }
+        pt::Expression::RationalNumberLiteral(loc, integer, fraction, exp, unit) => {
+            let unit = unit_literal(loc, unit, ns, diagnostics);
+
+            rational_number_literal(
+                loc,
+                integer,
+                fraction,
+                exp,
+                &-unit,
+                ns,
+                diagnostics,
+                resolve_to,
+            )
+        }
+        e => {
+            let expr = expression(e, context, ns, symtable, diagnostics, resolve_to)?;
+
+            used_variable(ns, &expr, symtable);
+
+            if let Some(expr) = user_defined_operator(
+                loc,
+                &[&expr],
+                pt::UserDefinedOperator::Negate,
+                diagnostics,
+                ns,
+            ) {
+                return Ok(expr);
+            }
+
+            let expr_type = expr.ty();
+
+            if let Expression::NumberLiteral { value, .. } = expr {
+                bigint_to_expression(loc, &-value, ns, diagnostics, resolve_to, None)
+            } else if let Expression::RationalNumberLiteral { ty, value: r, .. } = expr {
+                Ok(Expression::RationalNumberLiteral {
+                    loc: *loc,
+                    ty,
+                    value: -r,
+                })
+            } else {
+                type_bits_and_sign(&expr_type, loc, false, ns, diagnostics)?;
+
+                if !expr_type.is_signed_int(ns) {
+                    diagnostics.push(Diagnostic::error(
+                        *loc,
+                        "negate not allowed on unsigned".to_string(),
+                    ));
+                }
+
+                Ok(Expression::Negate {
+                    loc: *loc,
+                    ty: expr_type,
+                    expr: Box::new(expr),
+                })
+            }
+        }
+    }
+}
+
+fn less_equal(
+    l: &pt::Expression,
+    r: &pt::Expression,
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+    loc: &pt::Loc,
+) -> Result<Expression, ()> {
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    check_var_usage_expression(ns, &left, &right, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&left, &right],
+        pt::UserDefinedOperator::LessEqual,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
+    }
+
+    let ty = coerce_number(
+        &left.ty(),
+        &l.loc(),
+        &right.ty(),
+        &r.loc(),
+        true,
+        true,
+        ns,
+        diagnostics,
+    )?;
+
+    if ty.is_rational() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot use rational numbers with '<=' operator".into(),
+        ));
+        return Err(());
+    }
+
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+
+    let expr = Expression::LessEqual {
+        loc: *loc,
+        left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
+        right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
+    };
+
+    Ok(expr)
+}
+
+fn more_equal(
+    l: &pt::Expression,
+    r: &pt::Expression,
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+    loc: &pt::Loc,
+) -> Result<Expression, ()> {
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    check_var_usage_expression(ns, &left, &right, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&left, &right],
+        pt::UserDefinedOperator::MoreEqual,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
+    }
+
+    let ty = coerce_number(
+        &left.ty(),
+        &l.loc(),
+        &right.ty(),
+        &r.loc(),
+        true,
+        true,
+        ns,
+        diagnostics,
+    )?;
+
+    if ty.is_rational() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot use rational numbers with '>=' operator".into(),
+        ));
+        return Err(());
+    }
+
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+
+    let expr = Expression::MoreEqual {
+        loc: *loc,
+        left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
+        right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
+    };
+
+    Ok(expr)
+}
+
+fn less(
+    l: &pt::Expression,
+    r: &pt::Expression,
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+    loc: &pt::Loc,
+) -> Result<Expression, ()> {
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+
+    check_var_usage_expression(ns, &left, &right, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&left, &right],
+        pt::UserDefinedOperator::Less,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
+    }
+
+    let ty = coerce_number(
+        &left.ty(),
+        &l.loc(),
+        &right.ty(),
+        &r.loc(),
+        true,
+        true,
+        ns,
+        diagnostics,
+    )?;
+
+    if ty.is_rational() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot use rational numbers with '<' operator".into(),
+        ));
+        return Err(());
+    }
+
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+
+    let expr = Expression::Less {
+        loc: *loc,
+        left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
+        right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
+    };
+
+    Ok(expr)
+}
+
+fn more(
+    l: &pt::Expression,
+    r: &pt::Expression,
+    context: &ExprContext,
+    ns: &mut Namespace,
+    symtable: &mut Symtable,
+    diagnostics: &mut Diagnostics,
+    loc: &pt::Loc,
+) -> Result<Expression, ()> {
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
+
+    check_var_usage_expression(ns, &left, &right, symtable);
+
+    if let Some(expr) = user_defined_operator(
+        loc,
+        &[&left, &right],
+        pt::UserDefinedOperator::More,
+        diagnostics,
+        ns,
+    ) {
+        return Ok(expr);
+    }
+
+    let ty = coerce_number(
+        &left.ty(),
+        &l.loc(),
+        &right.ty(),
+        &r.loc(),
+        true,
+        true,
+        ns,
+        diagnostics,
+    )?;
+
+    if ty.is_rational() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "cannot use rational numbers with '>' operator".into(),
+        ));
+        return Err(());
+    }
+
+    let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+    let right = expression(r, context, ns, symtable, diagnostics, ResolveTo::Type(&ty))?;
+
+    let expr = Expression::More {
+        loc: *loc,
+        left: Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
+        right: Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
+    };
+
+    Ok(expr)
+}

+ 14 - 0
tests/codegen_testcases/solidity/const.sol

@@ -22,4 +22,18 @@ contract c {
 // CHECK: return uint32 16
 		return x;
 	}
+
+// BEGIN-CHECK: c::function::equal
+	function equal() public pure returns (bool) {
+		// should be const folded
+// CHECK: return true
+		return "abcd" == "abcd";
+	}
+
+// BEGIN-CHECK: c::function::not_equal
+	function not_equal() public pure returns (bool) {
+		// should be const folded
+// CHECK: return false
+		return "abcd" != "abcd";
+	}
 }

+ 17 - 0
tests/contract_testcases/evm/int_resolve_unknown.sol

@@ -0,0 +1,17 @@
+contract c {
+	uint16 public x = 0x10000 * 10 - 0x9ffff;
+
+	// When resolving the argument to require, we first 
+	// resolve it with ResolveTo::Unknown. Make sure this
+	// works correctly for integer expressions
+	function f(uint i, bytes4 b) public pure {
+		require(i < 2**225);
+		require(i > 255+255);
+		require(i >= 127*127);
+		require(i <= (2**127)*2);
+		require(b != 0);
+		require(b == 0);
+	}
+}
+
+// ---- Expect: diagnostics ----

+ 2 - 2
tests/contract_testcases/solana/rational_comparison.sol

@@ -23,7 +23,7 @@ contract c {
 // ---- Expect: diagnostics ----
 // error: 4:10-23: cannot use rational numbers with '>=' operator
 // error: 7:10-19: cannot use rational numbers with '>' operator
-// error: 10:10-19: cannot use rational numbers with '!=' or '==' operator
+// error: 10:10-19: cannot use rational numbers with '==' operator
 // error: 13:10-11: expression not allowed in constant rational number expression
 // error: 16:10-26: cannot use rational numbers with '<=' operator
-// error: 19:10-24: cannot use rational numbers with '!=' or '==' operator
+// error: 19:10-24: cannot use rational numbers with '!=' operator

+ 2 - 2
tests/contract_testcases/solana/rational_comparison2.sol

@@ -30,8 +30,8 @@ contract c {
 
 
 // ---- Expect: diagnostics ----
-// error: 3:10-20: cannot use rational numbers with '!=' or '==' operator
-// error: 7:10-20: cannot use rational numbers with '!=' or '==' operator
+// error: 3:10-20: cannot use rational numbers with '==' operator
+// error: 7:10-20: cannot use rational numbers with '!=' operator
 // error: 11:11-20: cannot use rational numbers with '<' operator
 // error: 15:16-26: cannot use rational numbers with '<=' operator
 // error: 19:7-14: cannot use rational numbers with '>' operator

+ 1 - 1
tests/evm.rs

@@ -248,7 +248,7 @@ fn ethereum_solidity_tests() {
         })
         .sum();
 
-    assert_eq!(errors, 1078);
+    assert_eq!(errors, 1072);
 }
 
 fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {