Ver código fonte

Implement reachable for inline assembly (#717)

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
Lucas Steuernagel 3 anos atrás
pai
commit
b0b0e8b5ad

+ 1 - 1
src/bin/languageserver/mod.rs

@@ -346,7 +346,7 @@ impl SolangServer {
                 }
             }
             ast::Statement::Underscore(_loc) => {}
-            ast::Statement::Assembly(_) => {
+            ast::Statement::Assembly(..) => {
                 //unimplemented!("Assembly block not implemented in language server");
             }
         }

+ 1 - 1
src/codegen/external_functions.rs

@@ -129,7 +129,7 @@ fn check_statement(stmt: &Statement, call_list: &mut Vec<usize>) -> bool {
         | Statement::Break(_)
         | Statement::Continue(_)
         | Statement::Underscore(_) => (),
-        Statement::Assembly(_) => {
+        Statement::Assembly(..) => {
             unimplemented!("Assembly block codegen not yet ready!");
         }
     }

+ 1 - 1
src/codegen/statements.rs

@@ -623,7 +623,7 @@ pub fn statement(
             }
         }
 
-        Statement::Assembly(_) => {
+        Statement::Assembly(..) => {
             unimplemented!("Assembly block codegen not yet ready");
         }
     }

+ 13 - 10
src/sema/ast.rs

@@ -1422,7 +1422,7 @@ pub enum Statement {
     },
     TryCatch(pt::Loc, bool, TryCatch),
     Underscore(pt::Loc),
-    Assembly(InlineAssembly),
+    Assembly(InlineAssembly, bool),
 }
 
 #[derive(Clone, Debug)]
@@ -1526,18 +1526,21 @@ impl Statement {
     pub fn reachable(&self) -> bool {
         match self {
             Statement::Block { statements, .. } => statements.iter().all(|s| s.reachable()),
-            Statement::Underscore(_) | Statement::Destructure(..) | Statement::VariableDecl(..) => {
-                true
-            }
+            Statement::Underscore(_)
+            | Statement::Destructure(..)
+            | Statement::VariableDecl(..)
+            | Statement::Emit { .. }
+            | Statement::Delete(..) => true,
+
+            Statement::Continue(_) | Statement::Break(_) | Statement::Return(..) => false,
+
             Statement::If(_, reachable, ..)
             | Statement::While(_, reachable, ..)
             | Statement::DoWhile(_, reachable, ..)
-            | Statement::Expression(_, reachable, _) => *reachable,
-            Statement::Emit { .. } => true,
-            Statement::Delete(..) => true,
-            Statement::Continue(_) | Statement::Break(_) | Statement::Return(..) => false,
-            Statement::For { reachable, .. } | Statement::TryCatch(_, reachable, _) => *reachable,
-            Statement::Assembly(_) => unimplemented!("Assembly block ast not ready"),
+            | Statement::Expression(_, reachable, _)
+            | Statement::For { reachable, .. }
+            | Statement::TryCatch(_, reachable, _)
+            | Statement::Assembly(_, reachable) => *reachable,
         }
     }
 }

+ 1 - 2
src/sema/dotgraphviz.rs

@@ -1529,8 +1529,7 @@ impl Dot {
                         Some(parent_rel),
                     );
                 }
-
-                Statement::Assembly(inline_assembly) => {
+                Statement::Assembly(inline_assembly, ..) => {
                     let labels = vec![
                         "inline assembly".to_string(),
                         ns.loc_to_string(&inline_assembly.loc),

+ 1 - 1
src/sema/mutability.rs

@@ -215,7 +215,7 @@ fn recurse_statements(stmts: &[Statement], state: &mut StateCheck) {
             }
             Statement::Emit { loc, .. } => state.write(loc),
             Statement::Break(_) | Statement::Continue(_) | Statement::Underscore(_) => (),
-            Statement::Assembly(_) => {
+            Statement::Assembly(..) => {
                 unimplemented!("Assembly block mutability not ready yet");
             }
         }

+ 1 - 1
src/sema/statements.rs

@@ -820,7 +820,7 @@ fn statement(
 
             let resolved_asm =
                 resolve_inline_assembly(loc, &block.statements, context, symtable, ns);
-            res.push(Statement::Assembly(resolved_asm.0));
+            res.push(Statement::Assembly(resolved_asm.0, resolved_asm.1));
             Ok(resolved_asm.1)
         }
         pt::Statement::Revert(loc, error, args) => {

+ 111 - 0
src/sema/tests/mod.rs

@@ -1,5 +1,8 @@
 #![cfg(test)]
+use crate::ast::{Expression, Parameter, Statement, TryCatch, Type};
+use crate::sema::assembly::ast::InlineAssembly;
 use crate::sema::expression::unescape;
+use solang_parser::pt::Loc;
 use solang_parser::Diagnostic;
 
 #[test]
@@ -14,3 +17,111 @@ fn test_unescape() {
     assert!(vec.is_empty());
     assert_eq!(res, vec![255]);
 }
+
+#[test]
+fn test_statement_reachable() {
+    let loc = Loc::File(0, 1, 2);
+    let test_cases: Vec<(Statement, bool)> = vec![
+        (Statement::Underscore(loc), true),
+        (
+            Statement::Destructure(loc, vec![], Expression::Undefined(Type::Bool)),
+            true,
+        ),
+        (
+            Statement::VariableDecl(
+                loc,
+                0,
+                Parameter {
+                    loc,
+                    name: None,
+                    ty: Type::Bool,
+                    ty_loc: Loc::Builtin,
+                    indexed: false,
+                    readonly: false,
+                },
+                None,
+            ),
+            true,
+        ),
+        (
+            Statement::Emit {
+                loc,
+                event_no: 0,
+                event_loc: Loc::Builtin,
+                args: vec![],
+            },
+            true,
+        ),
+        (
+            Statement::Delete(loc, Type::Bool, Expression::Undefined(Type::Bool)),
+            true,
+        ),
+        (Statement::Continue(loc), false),
+        (Statement::Break(loc), false),
+        (Statement::Return(loc, None), false),
+        (
+            Statement::If(
+                loc,
+                false,
+                Expression::Undefined(Type::Bool),
+                vec![],
+                vec![],
+            ),
+            false,
+        ),
+        (
+            Statement::While(loc, true, Expression::Undefined(Type::Bool), vec![]),
+            true,
+        ),
+        (
+            Statement::DoWhile(loc, false, vec![], Expression::Undefined(Type::Bool)),
+            false,
+        ),
+        (
+            Statement::Expression(loc, true, Expression::Undefined(Type::Bool)),
+            true,
+        ),
+        (
+            Statement::For {
+                loc,
+                reachable: false,
+                init: vec![],
+                cond: None,
+                next: vec![],
+                body: vec![],
+            },
+            false,
+        ),
+        (
+            Statement::TryCatch(
+                loc,
+                true,
+                TryCatch {
+                    expr: Expression::Poison,
+                    returns: vec![],
+                    ok_stmt: vec![],
+                    errors: vec![],
+                    catch_param: None,
+                    catch_param_pos: None,
+                    catch_stmt: vec![],
+                },
+            ),
+            true,
+        ),
+        (
+            Statement::Assembly(
+                InlineAssembly {
+                    loc,
+                    body: vec![],
+                    functions: vec![],
+                },
+                false,
+            ),
+            false,
+        ),
+    ];
+
+    for (test_case, expected) in test_cases {
+        assert_eq!(test_case.reachable(), expected);
+    }
+}

+ 16 - 0
tests/contract_testcases/substrate/assembly/unreachable_after_asm.dot

@@ -0,0 +1,16 @@
+strict digraph "tests/contract_testcases/substrate/assembly/unreachable_after_asm.sol" {
+	contract [label="contract testTypes\ntests/contract_testcases/substrate/assembly/unreachable_after_asm.sol:1:1-20"]
+	var [label="variable b\nvisibility internal\ntests/contract_testcases/substrate/assembly/unreachable_after_asm.sol:2:5-14"]
+	testAsm [label="function testAsm\ncontract: testTypes\ntests/contract_testcases/substrate/assembly/unreachable_after_asm.sol:3:5-51\nsignature testAsm(uint128[])\nvisibility public\nmutability nonpayable"]
+	parameters [label="parameters\nuint128[] vl"]
+	diagnostic [label="found contract ‘testTypes’\nlevel Debug\ntests/contract_testcases/substrate/assembly/unreachable_after_asm.sol:1:1-20"]
+	diagnostic_7 [label="evm assembly not supported on target substrate\nlevel Error\ntests/contract_testcases/substrate/assembly/unreachable_after_asm.sol:5:9-15:10"]
+	diagnostic_8 [label="unreachable statement\nlevel Error\ntests/contract_testcases/substrate/assembly/unreachable_after_asm.sol:17:9-19:10"]
+	contracts -> contract
+	contract -> var [label="variable"]
+	contract -> testAsm [label="function"]
+	testAsm -> parameters [label="parameters"]
+	diagnostics -> diagnostic [label="Debug"]
+	diagnostics -> diagnostic_7 [label="Error"]
+	diagnostics -> diagnostic_8 [label="Error"]
+}

+ 21 - 0
tests/contract_testcases/substrate/assembly/unreachable_after_asm.sol

@@ -0,0 +1,21 @@
+contract testTypes {
+    uint256 b;
+    function testAsm(uint128[] calldata vl) public {
+        uint256 y = 0;
+        assembly {
+            switch vl.length
+            case 1 {y := mul(b.slot, 2)}
+            case 2 {y := shr(b.offset, 2)}
+            default {
+                y := 5
+            }
+
+            y := sub(y, 1)
+            revert(y, 2)
+        }
+
+        if (vl[0] > 0) { 
+            b = 5;
+        }
+    }
+}