Quellcode durchsuchen

Allow signed literals to be explicitly casted to unsigned

Signed-off-by: Sean Young <sean@mess.org>
Sean Young vor 4 Jahren
Ursprung
Commit
596c46496d
2 geänderte Dateien mit 39 neuen und 8 gelöschten Zeilen
  1. 22 8
      src/sema/expression.rs
  2. 17 0
      tests/substrate_tests/primitives.rs

+ 22 - 8
src/sema/expression.rs

@@ -539,13 +539,27 @@ pub fn try_cast(
     match (&expr, &from, to) {
         (&Expression::NumberLiteral(_, _, ref n), p, &Type::Uint(to_len)) if p.is_primitive() => {
             return if n.sign() == Sign::Minus {
-                Err(Diagnostic::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion cannot change negative number to {}",
-                        to.to_string(ns)
-                    ),
-                ))
+                if implicit {
+                    Err(Diagnostic::type_error(
+                        *loc,
+                        format!(
+                            "implicit conversion cannot change negative number to {}",
+                            to.to_string(ns)
+                        ),
+                    ))
+                } else {
+                    // Convert to little endian so most significant bytes are at the end; that way
+                    // we can simply resize the vector to the right size
+                    let mut bs = n.to_signed_bytes_le();
+
+                    bs.resize(to_len as usize / 8, 0xff);
+
+                    Ok(Expression::NumberLiteral(
+                        *loc,
+                        Type::Uint(to_len),
+                        BigInt::from_bytes_le(Sign::Plus, &bs),
+                    ))
+                }
             } else if n.bits() >= to_len as u64 {
                 Err(Diagnostic::type_error(
                     *loc,
@@ -561,7 +575,7 @@ pub fn try_cast(
                     Type::Uint(to_len),
                     n.clone(),
                 ))
-            }
+            };
         }
         (&Expression::NumberLiteral(_, _, ref n), p, &Type::Int(to_len)) if p.is_primitive() => {
             return if n.bits() >= to_len as u64 {

+ 17 - 0
tests/substrate_tests/primitives.rs

@@ -808,3 +808,20 @@ fn implicit_bytes_cast_incompatible_size() {
 
     runtime.function("test", Vec::new());
 }
+
+#[test]
+fn signed_literal_unsigned_cast() {
+    let mut runtime = build_solidity(
+        r##"
+        contract test {
+            function foo() public {
+                assert(uint16(-1) == 0xffff);
+                assert(uint8(-2) == 0xfe);
+                assert(uint32(-3) == 0xffff_fffd);
+                assert(uint8(-4000) == 96);
+            }
+        }"##,
+    );
+
+    runtime.function("foo", Vec::new());
+}