Browse Source

Ensure that rational compares are not permitted (#1091)

This solidity was not rejected and causes a panic in emit, because
rationals should be compile-time constants.

```
function foo1(uint64 a, uint64 b) returns (bool) {
	return (a/b) >= 0.05;
}
```

This also improves the test performance by using `rayon`.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 3 years ago
parent
commit
035b15b596

+ 50 - 10
src/sema/expression.rs

@@ -1851,11 +1851,19 @@ pub fn expression(
                 diagnostics,
             )?;
 
-            Ok(Expression::More(
+            let expr = Expression::More(
                 *loc,
                 Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
                 Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            ))
+            );
+
+            if ty.is_rational() {
+                if let Err(diag) = eval_const_rational(&expr, ns) {
+                    diagnostics.push(diag);
+                }
+            }
+
+            Ok(expr)
         }
         pt::Expression::Less(loc, l, r) => {
             let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
@@ -1874,11 +1882,19 @@ pub fn expression(
                 diagnostics,
             )?;
 
-            Ok(Expression::Less(
+            let expr = Expression::Less(
                 *loc,
                 Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
                 Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            ))
+            );
+
+            if ty.is_rational() {
+                if let Err(diag) = eval_const_rational(&expr, ns) {
+                    diagnostics.push(diag);
+                }
+            }
+
+            Ok(expr)
         }
         pt::Expression::MoreEqual(loc, l, r) => {
             let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
@@ -1896,11 +1912,19 @@ pub fn expression(
                 diagnostics,
             )?;
 
-            Ok(Expression::MoreEqual(
+            let expr = Expression::MoreEqual(
                 *loc,
                 Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
                 Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            ))
+            );
+
+            if ty.is_rational() {
+                if let Err(diag) = eval_const_rational(&expr, ns) {
+                    diagnostics.push(diag);
+                }
+            }
+
+            Ok(expr)
         }
         pt::Expression::LessEqual(loc, l, r) => {
             let left = expression(l, context, ns, symtable, diagnostics, ResolveTo::Integer)?;
@@ -1918,11 +1942,19 @@ pub fn expression(
                 diagnostics,
             )?;
 
-            Ok(Expression::LessEqual(
+            let expr = Expression::LessEqual(
                 *loc,
                 Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
                 Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-            ))
+            );
+
+            if ty.is_rational() {
+                if let Err(diag) = eval_const_rational(&expr, ns) {
+                    diagnostics.push(diag);
+                }
+            }
+
+            Ok(expr)
         }
         pt::Expression::Equal(loc, l, r) => equal(loc, l, r, context, ns, symtable, diagnostics),
 
@@ -3905,11 +3937,19 @@ fn equal(
 
     let ty = coerce(&left_type, &l.loc(), &right_type, &r.loc(), ns, diagnostics)?;
 
-    Ok(Expression::Equal(
+    let expr = Expression::Equal(
         *loc,
         Box::new(left.cast(&l.loc(), &ty, true, ns, diagnostics)?),
         Box::new(right.cast(&r.loc(), &ty, true, ns, diagnostics)?),
-    ))
+    );
+
+    if ty.is_rational() {
+        if let Err(diag) = eval_const_rational(&expr, ns) {
+            diagnostics.push(diag);
+        }
+    }
+
+    Ok(expr)
 }
 
 /// Try string concatenation

+ 8 - 1
tests/contract.rs

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use path_slash::PathExt;
+use rayon::prelude::*;
 use solang::{codegen, file_resolver::FileResolver, parse_and_resolve, Target};
 use std::{
     ffi::OsStr,
@@ -33,6 +34,8 @@ fn contract_tests(file_path: &str, target: Target) -> io::Result<()> {
 }
 
 fn recurse_directory(path: PathBuf, target: Target) -> io::Result<()> {
+    let mut entries = Vec::new();
+
     for entry in read_dir(path)? {
         let path = entry?.path();
 
@@ -40,11 +43,15 @@ fn recurse_directory(path: PathBuf, target: Target) -> io::Result<()> {
             recurse_directory(path, target)?;
         } else if let Some(ext) = path.extension() {
             if ext.to_string_lossy() == "sol" {
-                parse_file(path, target)?;
+                entries.push(path);
             }
         }
     }
 
+    entries.into_par_iter().for_each(|entry| {
+        parse_file(entry, target).unwrap();
+    });
+
     Ok(())
 }
 

+ 116 - 0
tests/contract_testcases/solana/rational_comparison.dot

@@ -0,0 +1,116 @@
+strict digraph "tests/contract_testcases/solana/rational_comparison.sol" {
+	contract [label="contract c\ntests/contract_testcases/solana/rational_comparison.sol:2:1-21:2"]
+	foo1 [label="function foo1\ncontract: c\ntests/contract_testcases/solana/rational_comparison.sol:3:2-57\nsignature foo1(uint64,uint64)\nvisibility public\nmutability nonpayable"]
+	parameters [label="parameters\nuint64 a\nuint64 b"]
+	returns [label="returns\nbool "]
+	return [label="return\ntests/contract_testcases/solana/rational_comparison.sol:4:3-23"]
+	more_equal [label="more equal\ntests/contract_testcases/solana/rational_comparison.sol:4:10-23"]
+	cast [label="cast rational\ntests/contract_testcases/solana/rational_comparison.sol:4:11-14"]
+	divide [label="divide\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:4:11-14"]
+	variable [label="variable: a\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:4:11-12"]
+	variable_10 [label="variable: b\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:4:13-14"]
+	rational_literal [label="rational rational literal: 1/20\ntests/contract_testcases/solana/rational_comparison.sol:4:19-23"]
+	foo2 [label="function foo2\ncontract: c\ntests/contract_testcases/solana/rational_comparison.sol:6:2-57\nsignature foo2(uint64,uint64)\nvisibility public\nmutability nonpayable"]
+	parameters_13 [label="parameters\nuint64 a\nuint64 b"]
+	returns_14 [label="returns\nbool "]
+	return_15 [label="return\ntests/contract_testcases/solana/rational_comparison.sol:7:3-19"]
+	more [label="more\ntests/contract_testcases/solana/rational_comparison.sol:7:10-19"]
+	rational_literal_17 [label="rational rational literal: 11/5\ntests/contract_testcases/solana/rational_comparison.sol:7:10-15"]
+	cast_18 [label="cast rational\ntests/contract_testcases/solana/rational_comparison.sol:7:18-19"]
+	variable_19 [label="variable: a\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:7:18-19"]
+	foo3 [label="function foo3\ncontract: c\ntests/contract_testcases/solana/rational_comparison.sol:9:2-57\nsignature foo3(uint64,uint64)\nvisibility public\nmutability nonpayable"]
+	parameters_21 [label="parameters\nuint64 a\nuint64 b"]
+	returns_22 [label="returns\nbool "]
+	return_23 [label="return\ntests/contract_testcases/solana/rational_comparison.sol:10:3-19"]
+	equal [label="equal\ntests/contract_testcases/solana/rational_comparison.sol:10:10-19"]
+	rational_literal_25 [label="rational rational literal: 1\ntests/contract_testcases/solana/rational_comparison.sol:10:10-11"]
+	rational_literal_26 [label="rational rational literal: 1/20\ntests/contract_testcases/solana/rational_comparison.sol:10:15-19"]
+	foo4 [label="function foo4\ncontract: c\ntests/contract_testcases/solana/rational_comparison.sol:12:2-57\nsignature foo4(uint64,uint64)\nvisibility public\nmutability nonpayable"]
+	parameters_28 [label="parameters\nuint64 a\nuint64 b"]
+	returns_29 [label="returns\nbool "]
+	foo5 [label="function foo5\ncontract: c\ntests/contract_testcases/solana/rational_comparison.sol:15:2-57\nsignature foo5(uint64,uint64)\nvisibility public\nmutability nonpayable"]
+	parameters_31 [label="parameters\nuint64 a\nuint64 b"]
+	returns_32 [label="returns\nbool "]
+	return_33 [label="return\ntests/contract_testcases/solana/rational_comparison.sol:16:3-26"]
+	less_equal [label="less equal\ntests/contract_testcases/solana/rational_comparison.sol:16:10-26"]
+	cast_35 [label="cast rational\ntests/contract_testcases/solana/rational_comparison.sol:16:11-17"]
+	shift_left [label="shift left\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:16:11-17"]
+	variable_37 [label="variable: a\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:16:11-12"]
+	variable_38 [label="variable: b\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:16:16-17"]
+	rational_literal_39 [label="rational rational literal: 1/20\ntests/contract_testcases/solana/rational_comparison.sol:16:22-26"]
+	foo6 [label="function foo6\ncontract: c\ntests/contract_testcases/solana/rational_comparison.sol:18:2-57\nsignature foo6(uint64,uint64)\nvisibility public\nmutability nonpayable"]
+	parameters_41 [label="parameters\nuint64 a\nuint64 b"]
+	returns_42 [label="returns\nbool "]
+	return_43 [label="return\ntests/contract_testcases/solana/rational_comparison.sol:19:3-24"]
+	not [label="not\ntests/contract_testcases/solana/rational_comparison.sol:19:10-24"]
+	equal_45 [label="equal\ntests/contract_testcases/solana/rational_comparison.sol:19:10-24"]
+	rational_literal_46 [label="rational rational literal: 6/5\ntests/contract_testcases/solana/rational_comparison.sol:19:10-13"]
+	cast_47 [label="cast rational\ntests/contract_testcases/solana/rational_comparison.sol:19:18-23"]
+	bitwise_xor [label="bitwise xor\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:19:18-23"]
+	variable_49 [label="variable: a\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:19:18-19"]
+	variable_50 [label="variable: b\nuint64\ntests/contract_testcases/solana/rational_comparison.sol:19:22-23"]
+	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/solana/rational_comparison.sol:2:1-21:2"]
+	diagnostic_53 [label="expression not allowed in constant rational number expression\nlevel Error\ntests/contract_testcases/solana/rational_comparison.sol:4:10-23"]
+	diagnostic_54 [label="expression not allowed in constant rational number expression\nlevel Error\ntests/contract_testcases/solana/rational_comparison.sol:7:10-19"]
+	diagnostic_55 [label="expression not allowed in constant rational number expression\nlevel Error\ntests/contract_testcases/solana/rational_comparison.sol:10:10-19"]
+	diagnostic_56 [label="expression not allowed in constant rational number expression\nlevel Error\ntests/contract_testcases/solana/rational_comparison.sol:13:10-11"]
+	diagnostic_57 [label="expression not allowed in constant rational number expression\nlevel Error\ntests/contract_testcases/solana/rational_comparison.sol:16:10-26"]
+	diagnostic_58 [label="expression not allowed in constant rational number expression\nlevel Error\ntests/contract_testcases/solana/rational_comparison.sol:19:10-24"]
+	contracts -> contract
+	contract -> foo1 [label="function"]
+	foo1 -> parameters [label="parameters"]
+	foo1 -> returns [label="returns"]
+	foo1 -> return [label="body"]
+	return -> more_equal [label="expr"]
+	more_equal -> cast [label="left"]
+	cast -> divide [label="expr"]
+	divide -> variable [label="left"]
+	divide -> variable_10 [label="right"]
+	more_equal -> rational_literal [label="right"]
+	contract -> foo2 [label="function"]
+	foo2 -> parameters_13 [label="parameters"]
+	foo2 -> returns_14 [label="returns"]
+	foo2 -> return_15 [label="body"]
+	return_15 -> more [label="expr"]
+	more -> rational_literal_17 [label="left"]
+	more -> cast_18 [label="right"]
+	cast_18 -> variable_19 [label="expr"]
+	contract -> foo3 [label="function"]
+	foo3 -> parameters_21 [label="parameters"]
+	foo3 -> returns_22 [label="returns"]
+	foo3 -> return_23 [label="body"]
+	return_23 -> equal [label="expr"]
+	equal -> rational_literal_25 [label="left"]
+	equal -> rational_literal_26 [label="right"]
+	contract -> foo4 [label="function"]
+	foo4 -> parameters_28 [label="parameters"]
+	foo4 -> returns_29 [label="returns"]
+	contract -> foo5 [label="function"]
+	foo5 -> parameters_31 [label="parameters"]
+	foo5 -> returns_32 [label="returns"]
+	foo5 -> return_33 [label="body"]
+	return_33 -> less_equal [label="expr"]
+	less_equal -> cast_35 [label="left"]
+	cast_35 -> shift_left [label="expr"]
+	shift_left -> variable_37 [label="left"]
+	shift_left -> variable_38 [label="right"]
+	less_equal -> rational_literal_39 [label="right"]
+	contract -> foo6 [label="function"]
+	foo6 -> parameters_41 [label="parameters"]
+	foo6 -> returns_42 [label="returns"]
+	foo6 -> return_43 [label="body"]
+	return_43 -> not [label="expr"]
+	not -> equal_45 [label="expr"]
+	equal_45 -> rational_literal_46 [label="left"]
+	equal_45 -> cast_47 [label="right"]
+	cast_47 -> bitwise_xor [label="expr"]
+	bitwise_xor -> variable_49 [label="left"]
+	bitwise_xor -> variable_50 [label="right"]
+	diagnostics -> diagnostic [label="Debug"]
+	diagnostics -> diagnostic_53 [label="Error"]
+	diagnostics -> diagnostic_54 [label="Error"]
+	diagnostics -> diagnostic_55 [label="Error"]
+	diagnostics -> diagnostic_56 [label="Error"]
+	diagnostics -> diagnostic_57 [label="Error"]
+	diagnostics -> diagnostic_58 [label="Error"]
+}

+ 21 - 0
tests/contract_testcases/solana/rational_comparison.sol

@@ -0,0 +1,21 @@
+// Ensure that rational comparisons are not permitted
+contract c {
+	function foo1(uint64 a, uint64 b) public returns (bool) {
+		return (a/b) >= 0.05;
+	}
+	function foo2(uint64 a, uint64 b) public returns (bool) {
+		return 002.2 > a;
+	}
+	function foo3(uint64 a, uint64 b) public returns (bool) {
+		return 1 == 0.05;
+	}
+	function foo4(uint64 a, uint64 b) public returns (bool) {
+		return a*2.1 < b;
+	}
+	function foo5(uint64 a, uint64 b) public returns (bool) {
+		return (a << b) <= 0.05;
+	}
+	function foo6(uint64 a, uint64 b) public returns (bool) {
+		return 1.2 != (a ^ b);
+	}
+}

+ 1 - 1
tests/doc_examples.rs

@@ -12,7 +12,7 @@ use std::{
     fs::{read_dir, read_to_string},
 };
 
-/// Poppulates a file resolver with all imports that could be used by some example.
+/// Populates a file resolver with all imports that could be used by some example.
 fn file_resolver(target: Target) -> FileResolver {
     let mut result = FileResolver::new();
     result.set_file_contents(