فهرست منبع

Implement yul semantic analysis (#702)

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
Lucas Steuernagel 3 سال پیش
والد
کامیت
c8d60fcb24
38فایلهای تغییر یافته به همراه3695 افزوده شده و 656 حذف شده
  1. 61 21
      solang-parser/src/pt.rs
  2. 35 18
      solang-parser/src/solidity.lalrpop
  3. 339 310
      solang-parser/src/test.rs
  4. 2 2
      src/bin/languageserver/mod.rs
  5. 1 1
      src/codegen/external_functions.rs
  6. 1 1
      src/codegen/statements.rs
  7. 98 0
      src/sema/assembly/block.rs
  8. 80 0
      src/sema/assembly/builtin.rs
  9. 154 122
      src/sema/assembly/expression.rs
  10. 129 0
      src/sema/assembly/for_loop.rs
  11. 247 9
      src/sema/assembly/functions.rs
  12. 50 9
      src/sema/assembly/mod.rs
  13. 446 0
      src/sema/assembly/statements.rs
  14. 211 0
      src/sema/assembly/switch.rs
  15. 274 0
      src/sema/assembly/tests/block.rs
  16. 601 95
      src/sema/assembly/tests/expression.rs
  17. 153 0
      src/sema/assembly/tests/for_loop.rs
  18. 52 0
      src/sema/assembly/tests/functions.rs
  19. 29 1
      src/sema/assembly/tests/mod.rs
  20. 375 0
      src/sema/assembly/tests/statements.rs
  21. 113 0
      src/sema/assembly/tests/switch.rs
  22. 75 0
      src/sema/assembly/tests/types.rs
  23. 65 0
      src/sema/assembly/types.rs
  24. 5 4
      src/sema/ast.rs
  25. 1 0
      src/sema/contracts.rs
  26. 1 1
      src/sema/dotgraphviz.rs
  27. 2 0
      src/sema/expression.rs
  28. 1 0
      src/sema/mod.rs
  29. 1 1
      src/sema/mutability.rs
  30. 1 1
      src/sema/printer.rs
  31. 41 16
      src/sema/statements.rs
  32. 47 4
      src/sema/symtable.rs
  33. 3 2
      src/sema/unused_variable.rs
  34. 1 0
      src/sema/variables.rs
  35. 0 12
      tests/contract_testcases/solana/assembly/parse.dot
  36. 0 9
      tests/contract_testcases/solana/assembly/parse.sol
  37. 0 10
      tests/contract_testcases/solana/return_in_asm.dot
  38. 0 7
      tests/contract_testcases/solana/return_in_asm.sol

+ 61 - 21
solang-parser/src/pt.rs

@@ -608,7 +608,7 @@ pub enum Statement {
     Assembly {
         loc: Loc,
         dialect: Option<StringLiteral>,
-        statements: Vec<AssemblyStatement>,
+        block: AssemblyBlock,
     },
     Args(Loc, Vec<NamedArgument>),
     If(Loc, Expression, Box<Statement>, Option<Box<Statement>>),
@@ -651,28 +651,38 @@ pub enum AssemblyStatement {
         Vec<AssemblyTypedIdentifier>,
         Option<AssemblyExpression>,
     ),
-    Expression(AssemblyExpression),
-    If(Loc, AssemblyExpression, Vec<AssemblyStatement>),
-    For(
-        Loc,
-        Vec<AssemblyStatement>,
-        AssemblyExpression,
-        Vec<AssemblyStatement>,
-        Vec<AssemblyStatement>,
-    ),
-    Switch(
-        Loc,
-        AssemblyExpression,
-        Vec<AssemblySwitch>,
-        Option<AssemblySwitch>,
-    ),
+    If(Loc, AssemblyExpression, AssemblyBlock),
+    For(AssemblyFor),
+    Switch(AssemblySwitch),
     Leave(Loc),
     Break(Loc),
     Continue(Loc),
-    Block(Loc, Box<AssemblyStatement>),
+    Block(AssemblyBlock),
     FunctionDefinition(Box<AssemblyFunctionDefinition>),
     FunctionCall(Box<AssemblyFunctionCall>),
 }
+#[derive(PartialEq, Clone, Debug)]
+pub struct AssemblySwitch {
+    pub loc: Loc,
+    pub condition: AssemblyExpression,
+    pub cases: Vec<AssemblySwitchOptions>,
+    pub default: Option<AssemblySwitchOptions>,
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub struct AssemblyFor {
+    pub loc: Loc,
+    pub init_block: AssemblyBlock,
+    pub condition: AssemblyExpression,
+    pub post_block: AssemblyBlock,
+    pub execution_block: AssemblyBlock,
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct AssemblyBlock {
+    pub loc: Loc,
+    pub statements: Vec<AssemblyStatement>,
+}
 
 #[derive(Debug, PartialEq, Clone)]
 pub enum AssemblyExpression {
@@ -699,7 +709,7 @@ pub struct AssemblyFunctionDefinition {
     pub id: Identifier,
     pub params: Vec<AssemblyTypedIdentifier>,
     pub returns: Vec<AssemblyTypedIdentifier>,
-    pub body: Vec<AssemblyStatement>,
+    pub body: AssemblyBlock,
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -710,9 +720,17 @@ pub struct AssemblyFunctionCall {
 }
 
 #[derive(Debug, PartialEq, Clone)]
-pub enum AssemblySwitch {
-    Case(AssemblyExpression, Vec<AssemblyStatement>),
-    Default(Vec<AssemblyStatement>),
+pub enum AssemblySwitchOptions {
+    Case(Loc, AssemblyExpression, AssemblyBlock),
+    Default(Loc, AssemblyBlock),
+}
+
+impl CodeLocation for AssemblySwitchOptions {
+    fn loc(&self) -> Loc {
+        match self {
+            AssemblySwitchOptions::Case(loc, ..) | AssemblySwitchOptions::Default(loc, ..) => *loc,
+        }
+    }
 }
 
 impl CodeLocation for Statement {
@@ -737,3 +755,25 @@ impl CodeLocation for Statement {
         }
     }
 }
+
+impl AssemblyStatement {
+    pub fn loc(&self) -> Loc {
+        match self {
+            AssemblyStatement::Assign(loc, ..)
+            | AssemblyStatement::VariableDeclaration(loc, ..)
+            | AssemblyStatement::If(loc, ..)
+            | AssemblyStatement::Leave(loc, ..)
+            | AssemblyStatement::Break(loc, ..)
+            | AssemblyStatement::Continue(loc, ..) => *loc,
+
+            AssemblyStatement::Block(block) => block.loc,
+
+            AssemblyStatement::FunctionDefinition(func_def) => func_def.loc,
+
+            AssemblyStatement::FunctionCall(func_call) => func_call.loc,
+
+            AssemblyStatement::For(for_struct) => for_struct.loc,
+            AssemblyStatement::Switch(switch_struct) => switch_struct.loc,
+        }
+    }
+}

+ 35 - 18
solang-parser/src/solidity.lalrpop

@@ -754,11 +754,11 @@ NonIfStatement: Statement = {
     <l:@L> "unchecked" "{" <statements:Statement*> "}" <r:@R> => {
         Statement::Block { loc: Loc::File(file_no, l, r), unchecked: true, statements }
     },
-    <l:@L> "assembly" <dialect:StringLiteral?> <statements:AssemblyBlock> <r:@R> => {
+    <l:@L> "assembly" <dialect:StringLiteral?> <block:AssemblyBlock> <r:@R> => {
         Statement::Assembly {
             loc: Loc::File(file_no, l, r),
             dialect: dialect.map(|dialect| dialect.to_owned()),
-            statements
+            block
         }
     },
     <SimpleStatement> ";" => <>,
@@ -791,12 +791,13 @@ NonIfStatement: Statement = {
 AssemblyIdentifier: Identifier = {
     <l:@L> <n:identifier> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: n.to_string()},
     <l:@L> "return" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "return".to_string()},
-    <l:@L> "revert" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "revert".to_string()}
+    <l:@L> "revert" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "revert".to_string()},
+    <l:@L> "bool" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "bool".to_string()},
+    <l:@L> "address" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "address".to_string()},
 }
 
 AssemblyStatement: AssemblyStatement = {
-    <l:@L> "{" <statement:AssemblyStatement> "}" <r:@R> => { AssemblyStatement::Block(Loc::File(file_no, l, r),
-            Box::new(statement))},
+    <AssemblyBlock> => AssemblyStatement::Block(<>),
     <AssemblyVariableDeclaration> => <>,
     <AssemblyAssignment> => <>,
     <AssemblyFunctionCall> => AssemblyStatement::FunctionCall(Box::new(<>)),
@@ -815,8 +816,8 @@ AssemblyStatement: AssemblyStatement = {
     },
 }
 
-AssemblyBlock: Vec<AssemblyStatement> = {
-    "{" <AssemblyStatement*> "}" => <>,
+AssemblyBlock: AssemblyBlock = {
+    <l:@L> "{" <statements:AssemblyStatement*> "}" <r:@R> => AssemblyBlock{loc: Loc::File(file_no, l, r), statements},
 }
 
 AssemblyLiteral: AssemblyExpression = {
@@ -904,27 +905,43 @@ AssemblyType: AssemblyTypedIdentifier = {
 }
 
 AssemblySwitch: AssemblyStatement = {
-    <l:@L> "switch" <expr:AssemblyExpression> <default:AssemblySwitchDefault> <r:@R> =>
-    AssemblyStatement::Switch(Loc::File(file_no, l, r), expr, Vec::new(), Some(default)),
-    <l:@L> "switch" <expr:AssemblyExpression> <cases:AssemblySwitchCase+> <default:AssemblySwitchDefault?> <r:@R> =>
-    AssemblyStatement::Switch(Loc::File(file_no, l, r), expr, cases, default),
+    <l:@L> "switch" <condition:AssemblyExpression> <default:AssemblySwitchDefault> <r:@R> =>
+    AssemblyStatement::Switch(AssemblySwitch{
+        loc: Loc::File(file_no, l, r),
+        condition,
+        cases: Vec::new(),
+        default: Some(default)
+    }),
+    <l:@L> "switch" <condition:AssemblyExpression> <cases:AssemblySwitchCase+> <default:AssemblySwitchDefault?> <r:@R> =>
+    AssemblyStatement::Switch(AssemblySwitch{
+        loc: Loc::File(file_no, l, r),
+        condition,
+        cases,
+        default
+    }),
 }
 
-AssemblySwitchDefault: AssemblySwitch = {
-    "default" <body:AssemblyBlock> => {
-        AssemblySwitch::Default(body)
+AssemblySwitchDefault: AssemblySwitchOptions = {
+    <l:@L> "default" <body:AssemblyBlock> <r:@R> => {
+        AssemblySwitchOptions::Default(Loc::File(file_no, l, r), body)
     }
 }
 
-AssemblySwitchCase: AssemblySwitch = {
-    "case" <case:AssemblyLiteral> <body:AssemblyBlock> => {
-        AssemblySwitch::Case(case, body)
+AssemblySwitchCase: AssemblySwitchOptions = {
+    <l:@L> "case" <case:AssemblyLiteral> <body:AssemblyBlock> <r:@R> => {
+        AssemblySwitchOptions::Case(Loc::File(file_no, l, r), case, body)
     }
 }
 
 AssemblyFor: AssemblyStatement = {
     <l:@L> "for" <init:AssemblyBlock> <cond:AssemblyExpression> <post_iter:AssemblyBlock> <body:AssemblyBlock> <r:@R> => {
-        AssemblyStatement::For(Loc::File(file_no, l, r), init, cond, post_iter, body)
+        AssemblyStatement::For(AssemblyFor{
+            loc: Loc::File(file_no, l, r),
+            init_block: init,
+            condition: cond,
+            post_block: post_iter,
+            execution_block: body
+        })
     },
 }
 

+ 339 - 310
solang-parser/src/test.rs

@@ -633,236 +633,259 @@ fn test_assembly_parser() {
                 statements: vec![
                     Statement::Assembly {
                         loc: Loc::File(0, 54, 736),
-                        statements: vec![
-                            AssemblyStatement::VariableDeclaration(
-                                Loc::File(0, 98, 108),
-                                vec![AssemblyTypedIdentifier {
-                                    loc: Loc::File(0, 102, 103),
-                                    id: Identifier {
-                                        loc: Loc::File(0, 102, 103),
-                                        name: "x".to_string(),
-                                    },
-                                    ty: None,
-                                }],
-                                Some(AssemblyExpression::NumberLiteral(
-                                    Loc::File(0, 107, 108),
-                                    BigInt::from(0),
-                                    None,
-                                )),
-                            ),
-                            AssemblyStatement::For(
-                                Loc::File(0, 133, 388),
-                                vec![AssemblyStatement::VariableDeclaration(
-                                    Loc::File(0, 139, 149),
+                        block: AssemblyBlock {
+                            loc: Loc::File(0, 72, 736),
+                            statements: vec![
+                                AssemblyStatement::VariableDeclaration(
+                                    Loc::File(0, 98, 108),
                                     vec![AssemblyTypedIdentifier {
-                                        loc: Loc::File(0, 143, 144),
+                                        loc: Loc::File(0, 102, 103),
                                         id: Identifier {
-                                            loc: Loc::File(0, 143, 144),
-                                            name: "i".to_string(),
+                                            loc: Loc::File(0, 102, 103),
+                                            name: "x".to_string(),
                                         },
                                         ty: None,
                                     }],
                                     Some(AssemblyExpression::NumberLiteral(
-                                        Loc::File(0, 148, 149),
+                                        Loc::File(0, 107, 108),
                                         BigInt::from(0),
                                         None,
                                     )),
-                                )],
-                                AssemblyExpression::FunctionCall(Box::new(AssemblyFunctionCall {
-                                    loc: Loc::File(0, 152, 164),
-                                    id: Identifier {
-                                        loc: Loc::File(0, 152, 154),
-                                        name: "lt".to_string(),
-                                    },
-                                    arguments: vec![
-                                        AssemblyExpression::Variable(Identifier {
-                                            loc: Loc::File(0, 155, 156),
-                                            name: "i".to_string(),
-                                        }),
-                                        AssemblyExpression::HexNumberLiteral(
-                                            Loc::File(0, 158, 163),
-                                            "0x100".to_string(),
-                                            None,
-                                        ),
-                                    ],
-                                })),
-                                vec![AssemblyStatement::Assign(
-                                    Loc::File(0, 167, 184),
-                                    vec![AssemblyExpression::Variable(Identifier {
-                                        loc: Loc::File(0, 167, 168),
-                                        name: "i".to_string(),
-                                    })],
-                                    AssemblyExpression::FunctionCall(Box::new(
-                                        AssemblyFunctionCall {
-                                            loc: Loc::File(0, 172, 184),
-                                            id: Identifier {
-                                                loc: Loc::File(0, 172, 175),
-                                                name: "add".to_string(),
-                                            },
-                                            arguments: vec![
-                                                AssemblyExpression::Variable(Identifier {
-                                                    loc: Loc::File(0, 176, 177),
+                                ),
+                                AssemblyStatement::For(AssemblyFor{
+                                    loc: Loc::File(0, 133, 388),
+                                    init_block: AssemblyBlock{
+                                        loc: Loc::File(0, 137, 151),
+                                        statements: vec![AssemblyStatement::VariableDeclaration(
+                                            Loc::File(0, 139, 149),
+                                            vec![AssemblyTypedIdentifier {
+                                                loc: Loc::File(0, 143, 144),
+                                                id: Identifier {
+                                                    loc: Loc::File(0, 143, 144),
                                                     name: "i".to_string(),
-                                                }),
-                                                AssemblyExpression::HexNumberLiteral(
-                                                    Loc::File(0, 179, 183),
-                                                    "0x20".to_string(),
-                                                    None,
-                                                ),
-                                            ],
+                                                },
+                                                ty: None,
+                                            }],
+                                            Some(AssemblyExpression::NumberLiteral(
+                                                Loc::File(0, 148, 149),
+                                                BigInt::from(0),
+                                                None,
+                                            )),
+                                        )],
+                                    },
+                                    condition: AssemblyExpression::FunctionCall(Box::new(AssemblyFunctionCall {
+                                        loc: Loc::File(0, 152, 164),
+                                        id: Identifier {
+                                            loc: Loc::File(0, 152, 154),
+                                            name: "lt".to_string(),
                                         },
-                                    )),
-                                )],
-                                vec![
-                                    AssemblyStatement::Assign(
-                                        Loc::File(0, 217, 248),
-                                        vec![AssemblyExpression::Variable(Identifier {
-                                            loc: Loc::File(0, 217, 218),
-                                            name: "x".to_string(),
-                                        })],
-                                        AssemblyExpression::FunctionCall(Box::new(
-                                            AssemblyFunctionCall {
-                                                loc: Loc::File(0, 232, 248),
-                                                id: Identifier {
-                                                    loc: Loc::File(0, 232, 235),
-                                                    name: "add".to_string(),
+                                        arguments: vec![
+                                            AssemblyExpression::Variable(Identifier {
+                                                loc: Loc::File(0, 155, 156),
+                                                name: "i".to_string(),
+                                            }),
+                                            AssemblyExpression::HexNumberLiteral(
+                                                Loc::File(0, 158, 163),
+                                                "0x100".to_string(),
+                                                None,
+                                            ),
+                                        ],
+                                    })),
+                                    post_block: AssemblyBlock {
+                                        loc: Loc::File(0, 165, 186),
+                                        statements: vec![AssemblyStatement::Assign(
+                                            Loc::File(0, 167, 184),
+                                            vec![AssemblyExpression::Variable(Identifier {
+                                                loc: Loc::File(0, 167, 168),
+                                                name: "i".to_string(),
+                                            })],
+                                            AssemblyExpression::FunctionCall(Box::new(
+                                                AssemblyFunctionCall {
+                                                    loc: Loc::File(0, 172, 184),
+                                                    id: Identifier {
+                                                        loc: Loc::File(0, 172, 175),
+                                                        name: "add".to_string(),
+                                                    },
+                                                    arguments: vec![
+                                                        AssemblyExpression::Variable(Identifier {
+                                                            loc: Loc::File(0, 176, 177),
+                                                            name: "i".to_string(),
+                                                        }),
+                                                        AssemblyExpression::HexNumberLiteral(
+                                                            Loc::File(0, 179, 183),
+                                                            "0x20".to_string(),
+                                                            None,
+                                                        ),
+                                                    ],
                                                 },
-                                                arguments: vec![
-                                                    AssemblyExpression::Variable(Identifier {
-                                                        loc: Loc::File(0, 236, 237),
-                                                        name: "x".to_string(),
-                                                    }),
-                                                    AssemblyExpression::FunctionCall(Box::new(
-                                                        AssemblyFunctionCall {
-                                                            loc: Loc::File(0, 239, 247),
-                                                            id: Identifier {
-                                                                loc: Loc::File(0, 239, 244),
-                                                                name: "mload".to_string(),
-                                                            },
-                                                            arguments: vec![
-                                                                AssemblyExpression::Variable(
-                                                                    Identifier {
-                                                                        loc: Loc::File(0, 245, 246),
-                                                                        name: "i".to_string(),
+                                            )),
+                                        )],
+                                    },
+                                    execution_block: AssemblyBlock{
+                                        loc: Loc::File(0, 187, 388),
+                                        statements: vec![
+                                            AssemblyStatement::Assign(
+                                                Loc::File(0, 217, 248),
+                                                vec![AssemblyExpression::Variable(Identifier {
+                                                    loc: Loc::File(0, 217, 218),
+                                                    name: "x".to_string(),
+                                                })],
+                                                AssemblyExpression::FunctionCall(Box::new(
+                                                    AssemblyFunctionCall {
+                                                        loc: Loc::File(0, 232, 248),
+                                                        id: Identifier {
+                                                            loc: Loc::File(0, 232, 235),
+                                                            name: "add".to_string(),
+                                                        },
+                                                        arguments: vec![
+                                                            AssemblyExpression::Variable(Identifier {
+                                                                loc: Loc::File(0, 236, 237),
+                                                                name: "x".to_string(),
+                                                            }),
+                                                            AssemblyExpression::FunctionCall(Box::new(
+                                                                AssemblyFunctionCall {
+                                                                    loc: Loc::File(0, 239, 247),
+                                                                    id: Identifier {
+                                                                        loc: Loc::File(0, 239, 244),
+                                                                        name: "mload".to_string(),
                                                                     },
-                                                                ),
-                                                            ],
+                                                                    arguments: vec![
+                                                                        AssemblyExpression::Variable(
+                                                                            Identifier {
+                                                                                loc: Loc::File(0, 245, 246),
+                                                                                name: "i".to_string(),
+                                                                            },
+                                                                        ),
+                                                                    ],
+                                                                },
+                                                            )),
+                                                        ],
+                                                    },
+                                                )),
+                                            ),
+                                            AssemblyStatement::If(
+                                                Loc::File(0, 278, 362),
+                                                AssemblyExpression::FunctionCall(Box::new(
+                                                    AssemblyFunctionCall {
+                                                        loc: Loc::File(0, 281, 292),
+                                                        id: Identifier {
+                                                            loc: Loc::File(0, 281, 283),
+                                                            name: "gt".to_string(),
                                                         },
-                                                    )),
-                                                ],
-                                            },
-                                        )),
-                                    ),
-                                    AssemblyStatement::If(
-                                        Loc::File(0, 278, 362),
-                                        AssemblyExpression::FunctionCall(Box::new(
-                                            AssemblyFunctionCall {
-                                                loc: Loc::File(0, 281, 292),
-                                                id: Identifier {
-                                                    loc: Loc::File(0, 281, 283),
-                                                    name: "gt".to_string(),
+                                                        arguments: vec![
+                                                            AssemblyExpression::Variable(Identifier {
+                                                                loc: Loc::File(0, 284, 285),
+                                                                name: "i".to_string(),
+                                                            }),
+                                                            AssemblyExpression::HexNumberLiteral(
+                                                                Loc::File(0, 287, 291),
+                                                                "0x10".to_string(),
+                                                                None,
+                                                            ),
+                                                        ],
+                                                    },
+                                                )),
+                                                AssemblyBlock{
+                                                    loc: Loc::File(0, 293, 362),
+                                                    statements: vec![AssemblyStatement::Break(Loc::File(0, 327, 332))],
                                                 },
-                                                arguments: vec![
-                                                    AssemblyExpression::Variable(Identifier {
-                                                        loc: Loc::File(0, 284, 285),
-                                                        name: "i".to_string(),
-                                                    }),
-                                                    AssemblyExpression::HexNumberLiteral(
-                                                        Loc::File(0, 287, 291),
-                                                        "0x10".to_string(),
-                                                        None,
-                                                    ),
-                                                ],
+                                            ),
+                                        ],
+                                    },
+                            }),
+                                AssemblyStatement::VariableDeclaration(
+                                    Loc::File(0, 414, 451),
+                                    vec![
+                                        AssemblyTypedIdentifier {
+                                            loc: Loc::File(0, 418, 425),
+                                            id: Identifier {
+                                                loc: Loc::File(0, 418, 419),
+                                                name: "h".to_string(),
                                             },
-                                        )),
-                                        vec![AssemblyStatement::Break(Loc::File(0, 327, 332))],
-                                    ),
-                                ],
-                            ),
-                            AssemblyStatement::VariableDeclaration(
-                                Loc::File(0, 414, 451),
-                                vec![
-                                    AssemblyTypedIdentifier {
-                                        loc: Loc::File(0, 418, 425),
-                                        id: Identifier {
-                                            loc: Loc::File(0, 418, 419),
-                                            name: "h".to_string(),
+                                            ty: Some(Identifier {
+                                                loc: Loc::File(0, 422, 425),
+                                                name: "u32".to_string(),
+                                            }),
                                         },
-                                        ty: Some(Identifier {
-                                            loc: Loc::File(0, 422, 425),
-                                            name: "u32".to_string(),
-                                        }),
-                                    },
-                                    AssemblyTypedIdentifier {
-                                        loc: Loc::File(0, 427, 428),
-                                        id: Identifier {
+                                        AssemblyTypedIdentifier {
                                             loc: Loc::File(0, 427, 428),
-                                            name: "y".to_string(),
-                                        },
-                                        ty: None,
-                                    },
-                                    AssemblyTypedIdentifier {
-                                        loc: Loc::File(0, 430, 437),
-                                        id: Identifier {
-                                            loc: Loc::File(0, 430, 431),
-                                            name: "z".to_string(),
+                                            id: Identifier {
+                                                loc: Loc::File(0, 427, 428),
+                                                name: "y".to_string(),
+                                            },
+                                            ty: None,
                                         },
-                                        ty: Some(Identifier {
-                                            loc: Loc::File(0, 434, 437),
-                                            name: "u16".to_string(),
-                                        }),
-                                    },
-                                ],
-                                Some(AssemblyExpression::FunctionCall(Box::new(
-                                    AssemblyFunctionCall {
-                                        loc: Loc::File(0, 441, 451),
-                                        id: Identifier {
-                                            loc: Loc::File(0, 441, 449),
-                                            name: "funcCall".to_string(),
+                                        AssemblyTypedIdentifier {
+                                            loc: Loc::File(0, 430, 437),
+                                            id: Identifier {
+                                                loc: Loc::File(0, 430, 431),
+                                                name: "z".to_string(),
+                                            },
+                                            ty: Some(Identifier {
+                                                loc: Loc::File(0, 434, 437),
+                                                name: "u16".to_string(),
+                                            }),
                                         },
-                                        arguments: vec![],
-                                    },
-                                ))),
-                            ),
-                            AssemblyStatement::Switch(
-                                Loc::File(0, 477, 714),
-                                AssemblyExpression::Variable(Identifier {
-                                    loc: Loc::File(0, 484, 485),
-                                    name: "x".to_string(),
-                                }),
-                                vec![AssemblySwitch::Case(
-                                    AssemblyExpression::NumberLiteral(
-                                        Loc::File(0, 515, 516),
-                                        BigInt::from(0),
-                                        None,
-                                    ),
-                                    vec![AssemblyStatement::FunctionCall(Box::new(
+                                    ],
+                                    Some(AssemblyExpression::FunctionCall(Box::new(
                                         AssemblyFunctionCall {
-                                            loc: Loc::File(0, 547, 559),
+                                            loc: Loc::File(0, 441, 451),
                                             id: Identifier {
-                                                loc: Loc::File(0, 547, 553),
-                                                name: "revert".to_string(),
+                                                loc: Loc::File(0, 441, 449),
+                                                name: "funcCall".to_string(),
                                             },
-                                            arguments: vec![
-                                                AssemblyExpression::NumberLiteral(
-                                                    Loc::File(0, 554, 555),
-                                                    BigInt::from(0),
-                                                    None,
-                                                ),
-                                                AssemblyExpression::NumberLiteral(
-                                                    Loc::File(0, 557, 558),
-                                                    BigInt::from(0),
-                                                    None,
-                                                ),
-                                            ],
+                                            arguments: vec![],
                                         },
-                                    ))],
-                                )],
-                                Some(AssemblySwitch::Default(vec![AssemblyStatement::Leave(
-                                    Loc::File(0, 683, 688),
-                                )])),
-                            ),
-                        ],
+                                    ))),
+                                ),
+                                AssemblyStatement::Switch(AssemblySwitch{
+                                    loc: Loc::File(0, 477, 714),
+                                    condition: AssemblyExpression::Variable(Identifier {
+                                        loc: Loc::File(0, 484, 485),
+                                        name: "x".to_string(),
+                                    }),
+                                    cases: vec![AssemblySwitchOptions::Case(
+                                        Loc::File(0, 510, 620),
+                                        AssemblyExpression::NumberLiteral(
+                                            Loc::File(0, 515, 516),
+                                            BigInt::from(0),
+                                            None,
+                                        ),
+                                        AssemblyBlock{
+                                            loc: Loc::File(0, 517, 620),
+                                            statements: vec![AssemblyStatement::FunctionCall(Box::new(
+                                                AssemblyFunctionCall {
+                                                    loc: Loc::File(0, 547, 559),
+                                                    id: Identifier {
+                                                        loc: Loc::File(0, 547, 553),
+                                                        name: "revert".to_string(),
+                                                    },
+                                                    arguments: vec![
+                                                        AssemblyExpression::NumberLiteral(
+                                                            Loc::File(0, 554, 555),
+                                                            BigInt::from(0),
+                                                            None,
+                                                        ),
+                                                        AssemblyExpression::NumberLiteral(
+                                                            Loc::File(0, 557, 558),
+                                                            BigInt::from(0),
+                                                            None,
+                                                        ),
+                                                    ],
+                                                },
+                                            ))],
+                                        }
+                                    )],
+                                    default: Some(AssemblySwitchOptions::Default(
+                                        Loc::File(0, 645, 714),
+                                        AssemblyBlock {
+                                            loc: Loc::File(0, 653, 714),
+                                            statements: vec![AssemblyStatement::Leave(Loc::File(0, 683, 688))],
+                                        }
+                                    )),
+                            }),
+                            ],
+                        },
                         dialect: Some(StringLiteral {
                             loc: Loc::File(0, 63, 71),
                             string: "evmasm".to_string(),
@@ -870,117 +893,123 @@ fn test_assembly_parser() {
                     },
                     Statement::Assembly {
                         loc: Loc::File(0, 758, 1027),
-                        statements: vec![AssemblyStatement::FunctionDefinition(Box::new(
-                            AssemblyFunctionDefinition {
-                                loc: Loc::File(0, 794, 1005),
-                                id: Identifier {
-                                    loc: Loc::File(0, 803, 808),
-                                    name: "power".to_string(),
-                                },
-                                params: vec![
-                                    AssemblyTypedIdentifier {
-                                        loc: Loc::File(0, 809, 820),
-                                        id: Identifier {
-                                            loc: Loc::File(0, 809, 813),
-                                            name: "base".to_string(),
-                                        },
-                                        ty: Some(Identifier {
-                                            loc: Loc::File(0, 816, 820),
-                                            name: "u256".to_string(),
-                                        }),
-                                    },
-                                    AssemblyTypedIdentifier {
-                                        loc: Loc::File(0, 822, 830),
-                                        id: Identifier {
-                                            loc: Loc::File(0, 822, 830),
-                                            name: "exponent".to_string(),
-                                        },
-                                        ty: None,
-                                    },
-                                ],
-                                returns: vec![AssemblyTypedIdentifier {
-                                    loc: Loc::File(0, 835, 841),
+                        block: AssemblyBlock{
+                          loc: Loc::File(0, 767, 1027),
+                            statements: vec![AssemblyStatement::FunctionDefinition(Box::new(
+                                AssemblyFunctionDefinition {
+                                    loc: Loc::File(0, 794, 1005),
                                     id: Identifier {
-                                        loc: Loc::File(0, 835, 841),
-                                        name: "result".to_string(),
+                                        loc: Loc::File(0, 803, 808),
+                                        name: "power".to_string(),
                                     },
-                                    ty: None,
-                                }],
-                                body: vec![
-                                    AssemblyStatement::VariableDeclaration(
-                                        Loc::File(0, 896, 940),
-                                        vec![AssemblyTypedIdentifier {
-                                            loc: Loc::File(0, 900, 901),
+                                    params: vec![
+                                        AssemblyTypedIdentifier {
+                                            loc: Loc::File(0, 809, 820),
                                             id: Identifier {
-                                                loc: Loc::File(0, 900, 901),
-                                                name: "y".to_string(),
+                                                loc: Loc::File(0, 809, 813),
+                                                name: "base".to_string(),
                                             },
-                                            ty: None,
-                                        }],
-                                        Some(AssemblyExpression::FunctionCall(Box::new(
-                                            AssemblyFunctionCall {
-                                                loc: Loc::File(0, 905, 940),
-                                                id: Identifier {
-                                                    loc: Loc::File(0, 905, 908),
-                                                    name: "and".to_string(),
-                                                },
-                                                arguments: vec![
-                                                    AssemblyExpression::StringLiteral(
-                                                        StringLiteral {
-                                                            loc: Loc::File(0, 909, 914),
-                                                            string: "abc".to_string(),
-                                                        },
-                                                        Some(Identifier {
-                                                            loc: Loc::File(0, 915, 918),
-                                                            name: "u32".to_string(),
-                                                        }),
-                                                    ),
-                                                    AssemblyExpression::FunctionCall(Box::new(
-                                                        AssemblyFunctionCall {
-                                                            loc: Loc::File(0, 920, 939),
-                                                            id: Identifier {
-                                                                loc: Loc::File(0, 920, 923),
-                                                                name: "add".to_string(),
-                                                            },
-                                                            arguments: vec![
-                                                                AssemblyExpression::NumberLiteral(
-                                                                    Loc::File(0, 924, 930),
-                                                                    BigInt::from(3),
-                                                                    Some(Identifier {
-                                                                        loc: Loc::File(0, 926, 930),
-                                                                        name: "u256".to_string(),
-                                                                    }),
-                                                                ),
-                                                                AssemblyExpression::NumberLiteral(
-                                                                    Loc::File(0, 932, 938),
-                                                                    BigInt::from(2),
-                                                                    Some(Identifier {
-                                                                        loc: Loc::File(0, 934, 938),
-                                                                        name: "u256".to_string(),
-                                                                    }),
-                                                                ),
-                                                            ],
-                                                        },
-                                                    )),
-                                                ],
-                                            },
-                                        ))),
-                                    ),
-                                    AssemblyStatement::VariableDeclaration(
-                                        Loc::File(0, 969, 979),
-                                        vec![AssemblyTypedIdentifier {
-                                            loc: Loc::File(0, 973, 979),
+                                            ty: Some(Identifier {
+                                                loc: Loc::File(0, 816, 820),
+                                                name: "u256".to_string(),
+                                            }),
+                                        },
+                                        AssemblyTypedIdentifier {
+                                            loc: Loc::File(0, 822, 830),
                                             id: Identifier {
-                                                loc: Loc::File(0, 973, 979),
-                                                name: "result".to_string(),
+                                                loc: Loc::File(0, 822, 830),
+                                                name: "exponent".to_string(),
                                             },
                                             ty: None,
-                                        }],
-                                        None,
-                                    ),
-                                ],
-                            },
-                        ))],
+                                        },
+                                    ],
+                                    returns: vec![AssemblyTypedIdentifier {
+                                        loc: Loc::File(0, 835, 841),
+                                        id: Identifier {
+                                            loc: Loc::File(0, 835, 841),
+                                            name: "result".to_string(),
+                                        },
+                                        ty: None,
+                                    }],
+                                    body: AssemblyBlock {
+                                        loc: Loc::File(0, 866, 1005),
+                                        statements:  vec![
+                                            AssemblyStatement::VariableDeclaration(
+                                                Loc::File(0, 896, 940),
+                                                vec![AssemblyTypedIdentifier {
+                                                    loc: Loc::File(0, 900, 901),
+                                                    id: Identifier {
+                                                        loc: Loc::File(0, 900, 901),
+                                                        name: "y".to_string(),
+                                                    },
+                                                    ty: None,
+                                                }],
+                                                Some(AssemblyExpression::FunctionCall(Box::new(
+                                                    AssemblyFunctionCall {
+                                                        loc: Loc::File(0, 905, 940),
+                                                        id: Identifier {
+                                                            loc: Loc::File(0, 905, 908),
+                                                            name: "and".to_string(),
+                                                        },
+                                                        arguments: vec![
+                                                            AssemblyExpression::StringLiteral(
+                                                                StringLiteral {
+                                                                    loc: Loc::File(0, 909, 914),
+                                                                    string: "abc".to_string(),
+                                                                },
+                                                                Some(Identifier {
+                                                                    loc: Loc::File(0, 915, 918),
+                                                                    name: "u32".to_string(),
+                                                                }),
+                                                            ),
+                                                            AssemblyExpression::FunctionCall(Box::new(
+                                                                AssemblyFunctionCall {
+                                                                    loc: Loc::File(0, 920, 939),
+                                                                    id: Identifier {
+                                                                        loc: Loc::File(0, 920, 923),
+                                                                        name: "add".to_string(),
+                                                                    },
+                                                                    arguments: vec![
+                                                                        AssemblyExpression::NumberLiteral(
+                                                                            Loc::File(0, 924, 930),
+                                                                            BigInt::from(3),
+                                                                            Some(Identifier {
+                                                                                loc: Loc::File(0, 926, 930),
+                                                                                name: "u256".to_string(),
+                                                                            }),
+                                                                        ),
+                                                                        AssemblyExpression::NumberLiteral(
+                                                                            Loc::File(0, 932, 938),
+                                                                            BigInt::from(2),
+                                                                            Some(Identifier {
+                                                                                loc: Loc::File(0, 934, 938),
+                                                                                name: "u256".to_string(),
+                                                                            }),
+                                                                        ),
+                                                                    ],
+                                                                },
+                                                            )),
+                                                        ],
+                                                    },
+                                                ))),
+                                            ),
+                                            AssemblyStatement::VariableDeclaration(
+                                                Loc::File(0, 969, 979),
+                                                vec![AssemblyTypedIdentifier {
+                                                    loc: Loc::File(0, 973, 979),
+                                                    id: Identifier {
+                                                        loc: Loc::File(0, 973, 979),
+                                                        name: "result".to_string(),
+                                                    },
+                                                    ty: None,
+                                                }],
+                                                None,
+                                            ),
+                                        ],
+                                    }
+                                },
+                            ))],
+                        },
                         dialect: None,
                     },
                 ],

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

@@ -346,8 +346,8 @@ impl SolangServer {
                 }
             }
             ast::Statement::Underscore(_loc) => {}
-            ast::Statement::AssemblyBlock(_) => {
-                unimplemented!("Assembly block not implemented in language server");
+            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::AssemblyBlock(_) => {
+        Statement::Assembly(_) => {
             unimplemented!("Assembly block codegen not yet ready!");
         }
     }

+ 1 - 1
src/codegen/statements.rs

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

+ 98 - 0
src/sema/assembly/block.rs

@@ -0,0 +1,98 @@
+use crate::ast::Namespace;
+use crate::sema::assembly::functions::{process_function_header, FunctionsTable};
+use crate::sema::assembly::statements::{resolve_assembly_statement, AssemblyStatement};
+use crate::sema::expression::ExprContext;
+use crate::sema::symtable::{LoopScopes, Symtable};
+use solang_parser::{pt, Diagnostic};
+
+#[derive(Debug, Clone)]
+pub struct AssemblyBlock {
+    pub loc: pt::Loc,
+    pub body: Vec<(AssemblyStatement, bool)>,
+}
+
+/// Resolve an assembly block.
+/// Returns the resolved block and a boolean that tells us if the next statement is reachable.
+pub fn resolve_assembly_block(
+    loc: &pt::Loc,
+    statements: &[pt::AssemblyStatement],
+    context: &ExprContext,
+    mut reachable: bool,
+    loop_scope: &mut LoopScopes,
+    function_table: &mut FunctionsTable,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> (AssemblyBlock, bool) {
+    function_table.new_scope();
+    symtable.new_scope();
+
+    let (body, local_reachable) = process_statements(
+        statements,
+        context,
+        reachable,
+        symtable,
+        loop_scope,
+        function_table,
+        ns,
+    );
+
+    reachable &= local_reachable;
+
+    symtable.leave_scope();
+    function_table.leave_scope();
+
+    (AssemblyBlock { loc: *loc, body }, reachable)
+}
+
+/// Resolves an array of assembly statements.
+/// Returns a vector of tuples (resolved_statement, reachable) and a boolean that tells us if the
+/// next statement is reachable
+pub(crate) fn process_statements(
+    statements: &[pt::AssemblyStatement],
+    context: &ExprContext,
+    mut reachable: bool,
+    symtable: &mut Symtable,
+    loop_scope: &mut LoopScopes,
+    functions_table: &mut FunctionsTable,
+    ns: &mut Namespace,
+) -> (Vec<(AssemblyStatement, bool)>, bool) {
+    let mut func_count: usize = 0;
+    for item in statements {
+        if let pt::AssemblyStatement::FunctionDefinition(fun_def) = item {
+            process_function_header(fun_def, functions_table, ns);
+            func_count += 1;
+        }
+    }
+
+    let mut body: Vec<(AssemblyStatement, bool)> =
+        Vec::with_capacity(statements.len() - func_count);
+    let mut has_unreachable = false;
+    for item in statements {
+        match resolve_assembly_statement(
+            item,
+            context,
+            reachable,
+            loop_scope,
+            symtable,
+            &mut body,
+            functions_table,
+            ns,
+        ) {
+            Ok(can_reach_next_statement) => {
+                if !reachable && !has_unreachable {
+                    ns.diagnostics.push(Diagnostic::warning(
+                        item.loc(),
+                        "unreachable assembly statement".to_string(),
+                    ));
+                    has_unreachable = true;
+                }
+                reachable &= can_reach_next_statement;
+            }
+            Err(_) => {
+                break;
+            }
+        }
+    }
+
+    (body, reachable)
+}

+ 80 - 0
src/sema/assembly/builtin.rs

@@ -6,6 +6,7 @@ pub struct AssemblyBuiltinPrototype {
     pub no_returns: u8,
     pub doc: &'static str,
     pub ty: AssemblyBuiltInFunction,
+    pub stops_execution: bool,
 }
 
 // The enums declaration order should match that of the static vector containing the builtins
@@ -96,6 +97,7 @@ static UNSUPPORTED_BUILTINS: phf::Set<&'static str> = phf_set! {
     "linkersymbol", "memoryguard"
 };
 
+/// Checks if bultin function is unsupported
 pub(crate) fn assembly_unsupported_builtin(name: &str) -> bool {
     UNSUPPORTED_BUILTINS.contains(name)
 }
@@ -179,11 +181,13 @@ static BUILTIN_ASSEMBLY_FUNCTIONS: phf::Map<&'static str, AssemblyBuiltInFunctio
     "gaslimit" => AssemblyBuiltInFunction::GasLimit,
 };
 
+/// Retrieved the builtin function type from an identifier name
 pub fn parse_builtin_keyword(keyword: &str) -> Option<&AssemblyBuiltInFunction> {
     BUILTIN_ASSEMBLY_FUNCTIONS.get(keyword)
 }
 
 impl AssemblyBuiltInFunction {
+    /// Retrieve the prototype from the enum type
     pub(crate) fn get_prototype_info(self) -> &'static AssemblyBuiltinPrototype {
         let index = self as usize;
         &ASSEMBLY_BUILTIN[index]
@@ -200,6 +204,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "Stop execution",
             ty: AssemblyBuiltInFunction::Stop,
+            stops_execution: true,
         },
         AssemblyBuiltinPrototype{
             name: "add",
@@ -207,6 +212,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "add(x, y) returns x + y",
             ty: AssemblyBuiltInFunction::Add,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "sub",
@@ -214,6 +220,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "sub(x, y) returns x - y",
             ty: AssemblyBuiltInFunction::Sub,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "mul",
@@ -221,6 +228,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "mul(x, y) returns x*y",
             ty: AssemblyBuiltInFunction::Mul,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "div",
@@ -228,6 +236,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "div(x, y) returns x/y or 0 if y == 0",
             ty: AssemblyBuiltInFunction::Div,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "sdiv",
@@ -235,6 +244,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "sdiv(x, y) returns x/y or 0 if y==0. Used for signed numbers in two's complement",
             ty: AssemblyBuiltInFunction::SDiv,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "mod",
@@ -242,6 +252,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "mod(x, y) returns x % y or 0 if y == 0",
             ty: AssemblyBuiltInFunction::Mod,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "smod",
@@ -249,6 +260,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "smod(x, y) returns x % y or 0 if y == 0. Used for signed numbers in two's complement",
             ty: AssemblyBuiltInFunction::SMod,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "exp",
@@ -256,6 +268,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "exp(x, y) returns x to the power of y",
             ty: AssemblyBuiltInFunction::Exp,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "not",
@@ -263,6 +276,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "not(x): bitwise \"not\" of x (every bit is negated)",
             ty: AssemblyBuiltInFunction::Not,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "lt",
@@ -270,6 +284,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "lt(x, y) returns 1 if x < y, 0 otherwise",
             ty: AssemblyBuiltInFunction::Lt,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "gt",
@@ -277,6 +292,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "gt(x, y) returns 1 if x > y, 0 otherwise",
             ty: AssemblyBuiltInFunction::Gt,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "slt",
@@ -284,6 +300,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "slt(x, y) returns 1 if x > y, 0 otherwise. Used for signed numbers in two's complement",
             ty: AssemblyBuiltInFunction::Slt,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "sgt",
@@ -291,6 +308,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "sgt(x, y) returns 1 if x > y, 0 otherwise. Used for signed numbers in two's complement",
             ty: AssemblyBuiltInFunction::Sgt,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "eq",
@@ -298,6 +316,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "eq(x, y) returns 1 if x == y, 0 otherwise",
             ty: AssemblyBuiltInFunction::Eq,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "iszero",
@@ -305,6 +324,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "iszero(x) returns 1 if x == 0, 0 otherwise",
             ty: AssemblyBuiltInFunction::IsZero,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "and",
@@ -312,6 +332,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "and(x, y) returns the bitwise \"and\" between x and y",
             ty: AssemblyBuiltInFunction::And,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "or",
@@ -319,6 +340,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "or(x, y) returns the bitwise \"or\" between x and y",
             ty: AssemblyBuiltInFunction::Or,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "xor",
@@ -326,6 +348,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "xor(x, y) returns the bitwise \"xor\" between x and y",
             ty: AssemblyBuiltInFunction::Xor,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "byte",
@@ -333,6 +356,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "byte(n, x) returns the nth byte of x, where the most significant byt is the 0th",
             ty: AssemblyBuiltInFunction::Byte,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "shl",
@@ -340,6 +364,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "shl(x, y) returns the logical shift left of y by x bits",
             ty: AssemblyBuiltInFunction::Shl,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "shr",
@@ -347,6 +372,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "shr(x, y) returns the logical shift right of y by x bits",
             ty: AssemblyBuiltInFunction::Shr,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "sar",
@@ -354,6 +380,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "signed arithmetic shift right y by x bits",
             ty: AssemblyBuiltInFunction::Sar,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "addmod",
@@ -361,6 +388,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "addmod(x, y, m) returns (x + y) % m or 0 if m == 0",
             ty: AssemblyBuiltInFunction::AddMod,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "mulmod",
@@ -368,6 +396,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "mulmod(x, y, m) returns (x * y) % m or 0 if m == 0",
             ty: AssemblyBuiltInFunction::MulMod,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "signextend",
@@ -375,6 +404,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "signextend(i, x) sign extends from (i*8+7)th bit counting from least significant",
             ty: AssemblyBuiltInFunction::SignExtend,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "keccak256",
@@ -382,6 +412,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "keccak256(p, n) performs keccak(mem[p...(p+n)])",
             ty: AssemblyBuiltInFunction::Keccak256,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "pc",
@@ -389,6 +420,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the current position in code, i.e. the program counter",
             ty: AssemblyBuiltInFunction::Pc,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "pop",
@@ -396,6 +428,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "pop(x) discard value x",
             ty: AssemblyBuiltInFunction::Pop,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "mload",
@@ -403,6 +436,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "mload(p) returns mem[p...(p+32)]",
             ty: AssemblyBuiltInFunction::MLoad,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "mstore",
@@ -410,6 +444,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "mstore(p, v) stores v into mem[p...(p+32)]",
             ty: AssemblyBuiltInFunction::MStore,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "mstore8",
@@ -417,6 +452,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "mstore8(p, v) stores (v & 0xff) into mem[p] (modified a single byte of v)",
             ty: AssemblyBuiltInFunction::MStore8,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "sload",
@@ -424,6 +460,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "sload(p) returns storage[p], i.e. memory on contract's storage",
             ty: AssemblyBuiltInFunction::SLoad,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "sstore",
@@ -431,6 +468,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "sstore(p) stores v into storage[p]",
             ty: AssemblyBuiltInFunction::SStore,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "msize",
@@ -438,6 +476,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the size of memory, i.e largest accessed memory index",
             ty: AssemblyBuiltInFunction::MSize,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "gas",
@@ -445,6 +484,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns gas still available to execution",
             ty: AssemblyBuiltInFunction::Gas,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "address",
@@ -452,6 +492,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the address of the current contract / execution context",
             ty: AssemblyBuiltInFunction::Address,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "balance",
@@ -459,6 +500,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "balance(a) returns the wei balance at address a",
             ty: AssemblyBuiltInFunction::Balance,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "selfbalance",
@@ -466,6 +508,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the wei balance at the address of the current contract / execution context",
             ty: AssemblyBuiltInFunction::SelfBalance,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "caller",
@@ -473,6 +516,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the call sender",
             ty: AssemblyBuiltInFunction::Caller,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "callvalue",
@@ -480,6 +524,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the wei sent together with the current call",
             ty: AssemblyBuiltInFunction::CallValue,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "calldataload",
@@ -487,6 +532,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "calldataload(p) returns call data starting from position p (32 bytes)",
             ty: AssemblyBuiltInFunction::CallDataLoad,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "calldatasize",
@@ -494,6 +540,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the size of call data in bytes",
             ty: AssemblyBuiltInFunction::CallDataSize,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "calldatacopy",
@@ -501,6 +548,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "calldatacopy(t, f, s) copies s bytes from calldata at position f to mem at position t",
             ty: AssemblyBuiltInFunction::CallDataCopy,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "codesize",
@@ -508,6 +556,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the size of the current contract / execution context",
             ty: AssemblyBuiltInFunction::CodeSize,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "codecopy",
@@ -515,6 +564,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "codecopy(t, f, s) copies s bytes from code at position f to mem at position t",
             ty: AssemblyBuiltInFunction::CodeCopy,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "extcodesize",
@@ -522,6 +572,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "extcodesize(a) returns the size of the code at address a",
             ty: AssemblyBuiltInFunction::ExtCodeSize,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "extcodecopy",
@@ -529,6 +580,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "extcodecopy(a, t, f, s) copies s bytes from code located at address a at position f to mem at position t",
             ty: AssemblyBuiltInFunction::ExtCodeCopy,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "returndatasize",
@@ -536,6 +588,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the size of the last returndata",
             ty: AssemblyBuiltInFunction::ReturnDataSize,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "returndatacopy",
@@ -543,6 +596,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "returndatacopy(t, f, s) copy s bytes from return data at position f to mem at position t",
             ty: AssemblyBuiltInFunction::ReturnDataCopy,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "extcodehash",
@@ -550,6 +604,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "extcodehash(a) returns the code hash of address a",
             ty: AssemblyBuiltInFunction::ExtCodeHash,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "create",
@@ -557,6 +612,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "create(v, p, n) creates new contract with code mem[p..(p+n)] and sends v wei. It returns the new address or 0 on error",
             ty: AssemblyBuiltInFunction::Create,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "create2",
@@ -564,6 +620,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "create2(v, p, n, s) new contract with code mem[p...(p+n)] at address keccak256(0xff . this . s . keccak256(mem[p...(p+n)]) and sends v wei.\n 0xff is a 1 byte value, 'this' is the current contract's address as a 20 byte value and 's' is a big endian 256-bit value. it returns 0 on error.",
             ty: AssemblyBuiltInFunction::Create2,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "call",
@@ -571,6 +628,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "call(g, a, v, in, insize, out, outsize) calls contract at address a with input mem[in...(in+insize)] providing f cas and v wei and outputs area mem[out...(out+outsize)]. It returns 0 on error and 1 on success",
             ty: AssemblyBuiltInFunction::Call,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "callcode",
@@ -578,6 +636,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Identical to call(g, a, v, in, insize, out, outsize), but only use the code from a and stay in the context of the current contract otherwise",
             ty: AssemblyBuiltInFunction::CallCode,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "delegatecall",
@@ -585,6 +644,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Identical to 'callcode' but also keep caller and callvalue",
             ty: AssemblyBuiltInFunction::DelegateCall,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "staticcall",
@@ -592,6 +652,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Identical to call(g, a, 0, in, insize, out, outsize), but do not allow state modifications",
             ty: AssemblyBuiltInFunction::StaticCall,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "return",
@@ -599,6 +660,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "return(p, s) ends execution and returns data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Return,
+            stops_execution: true,
         },
         AssemblyBuiltinPrototype{
             name: "revert",
@@ -606,6 +668,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "revert(p, s) ends execution, reverts state changes and returns data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Revert,
+            stops_execution: true,
         },
         AssemblyBuiltinPrototype{
             name: "selfdestruct",
@@ -613,6 +676,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "selfdestruct(a) ends execution, destroy current contract and sends funds to a",
             ty: AssemblyBuiltInFunction::SelfDestruct,
+            stops_execution: true,
         },
         AssemblyBuiltinPrototype{
             name: "invalid",
@@ -620,6 +684,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "Ends execution with invalid instruction",
             ty: AssemblyBuiltInFunction::Invalid,
+            stops_execution: true,
         },
         AssemblyBuiltinPrototype{
             name: "log0",
@@ -627,6 +692,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "log(p, s): log without topics and data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Log0,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "log1",
@@ -634,6 +700,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "log1(p, s, t1): log with topic t1 and data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Log1,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "log2",
@@ -641,6 +708,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "log2(p, s, t1, t2): log with topics t1, t2 and data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Log2,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "log3",
@@ -648,6 +716,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "log3(p, s, t1, t2, t3): log with topics t1, t2, t3 and data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Log3,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "log4",
@@ -655,6 +724,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 0,
             doc: "log4(p, s, t1, t2, t3, t4): log with topics t1, t2, t3, t4 with data mem[p...(p+s)]",
             ty: AssemblyBuiltInFunction::Log4,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "chainid",
@@ -662,6 +732,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the ID of the executing chain",
             ty: AssemblyBuiltInFunction::ChainId,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "basefee",
@@ -669,6 +740,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Return the current block's base fee",
             ty: AssemblyBuiltInFunction::BaseFee,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "origin",
@@ -676,6 +748,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the transaction sender",
             ty: AssemblyBuiltInFunction::Origin,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "gasprice",
@@ -683,6 +756,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the gas price of the transaction",
             ty: AssemblyBuiltInFunction::GasPrice,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "blockhash",
@@ -690,6 +764,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "blockhash(b) return the hash of block #b - only valid for the last 256 executing block excluding current",
             ty: AssemblyBuiltInFunction::BlockHash,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "coinbase",
@@ -697,6 +772,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the current mining beneficiary",
             ty: AssemblyBuiltInFunction::CoinBase,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "timestamp",
@@ -704,6 +780,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the timestamp of the current block in seconds since the epoch",
             ty: AssemblyBuiltInFunction::Timestamp,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "number",
@@ -711,6 +788,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the current block's number",
             ty: AssemblyBuiltInFunction::Number,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "difficulty",
@@ -718,6 +796,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the difficulty of the current block",
             ty: AssemblyBuiltInFunction::Difficulty,
+            stops_execution: false,
         },
         AssemblyBuiltinPrototype{
             name: "gaslimit",
@@ -725,6 +804,7 @@ static ASSEMBLY_BUILTIN: [AssemblyBuiltinPrototype; 76] =
             no_returns: 1,
             doc: "Returns the current block's gas limit",
             ty: AssemblyBuiltInFunction::GasLimit,
+            stops_execution: false,
         },
     ];
 

+ 154 - 122
src/sema/assembly/expression.rs

@@ -2,21 +2,21 @@ use crate::ast::{Namespace, Symbol, Type};
 use crate::sema::assembly::builtin::{
     assembly_unsupported_builtin, parse_builtin_keyword, AssemblyBuiltInFunction,
 };
-use crate::sema::assembly::functions::{AssemblyFunction, AssemblyFunctionParameter};
-use crate::sema::assembly::types::{get_default_type_from_identifier, get_type_from_string};
+use crate::sema::assembly::functions::{AssemblyFunctionParameter, FunctionsTable};
+use crate::sema::assembly::types::{
+    get_default_type_from_identifier, get_type_from_string, verify_type_from_expression,
+};
 use crate::sema::expression::{unescape, ExprContext};
 use crate::sema::symtable::{Symtable, VariableUsage};
-use indexmap::IndexMap;
 use num_bigint::{BigInt, Sign};
 use num_traits::Num;
 use solang_parser::diagnostics::{ErrorType, Level};
 use solang_parser::pt::{AssemblyFunctionCall, CodeLocation, Identifier, Loc, StorageLocation};
 use solang_parser::{pt, Diagnostic};
 
-// TODO: State variables cannot be assigned to, only .slot can be assigned to, .length cannot be assigned to (check TypeChecker.cpp)
 // TODO: Add assembly to unused variable detection
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Debug, Clone)]
 pub enum AssemblyExpression {
     BoolLiteral(pt::Loc, bool, Type),
     NumberLiteral(pt::Loc, BigInt, Type),
@@ -26,11 +26,11 @@ pub enum AssemblyExpression {
     ConstantVariable(pt::Loc, Type, Option<usize>, usize),
     StorageVariable(pt::Loc, Type, usize, usize),
     BuiltInCall(pt::Loc, AssemblyBuiltInFunction, Vec<AssemblyExpression>),
-    FunctionCall(pt::Loc, String, Vec<AssemblyExpression>),
+    FunctionCall(pt::Loc, usize, Vec<AssemblyExpression>),
     MemberAccess(pt::Loc, Box<AssemblyExpression>, AssemblySuffix),
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Debug, Clone)]
 pub enum AssemblySuffix {
     Offset,
     Slot,
@@ -39,6 +39,7 @@ pub enum AssemblySuffix {
     Address,
 }
 
+/// Given a keyword, returns the suffix it represents in YUL
 fn get_suffix_from_string(suffix_name: &str) -> Option<AssemblySuffix> {
     match suffix_name {
         "offset" => Some(AssemblySuffix::Offset),
@@ -67,13 +68,12 @@ impl CodeLocation for AssemblyExpression {
     }
 }
 
-// TODO: remove this decorator. It avoids warnings during development
-#[allow(dead_code)]
+/// Resolve an assembly expression.
 pub(crate) fn resolve_assembly_expression(
     expr: &pt::AssemblyExpression,
     context: &ExprContext,
     symtable: &Symtable,
-    functions: &IndexMap<String, AssemblyFunction>,
+    function_table: &FunctionsTable,
     ns: &mut Namespace,
 ) -> Result<AssemblyExpression, ()> {
     match expr {
@@ -121,15 +121,16 @@ pub(crate) fn resolve_assembly_expression(
         }
 
         pt::AssemblyExpression::FunctionCall(func_call) => {
-            resolve_function_call(functions, func_call, context, symtable, ns)
+            resolve_function_call(function_table, func_call, context, symtable, ns)
         }
 
         pt::AssemblyExpression::Member(loc, expr, id) => {
-            resolve_member_access(loc, expr, id, context, symtable, functions, ns)
+            resolve_member_access(loc, expr, id, context, symtable, function_table, ns)
         }
     }
 }
 
+/// Returns the default YUL type a bigint represents
 fn get_type_from_big_int(big_int: &BigInt) -> Type {
     match big_int.sign() {
         Sign::Minus => Type::Int(256),
@@ -173,7 +174,7 @@ fn resolve_number_literal(
                     pos: *loc,
                     level: Level::Error,
                     ty: ErrorType::TypeError,
-                    message: "singed value cannot fit in unsigned type".to_string(),
+                    message: "signed integer cannot fit in unsigned integer".to_string(),
                     notes: vec![],
                 });
                 return Err(());
@@ -302,54 +303,71 @@ fn resolve_variable_reference(
         }
     }
 
-    match ns.resolve_var(context.file_no, context.contract_no, id, false) {
-        Some(Symbol::Variable(_, Some(var_contract_no), var_no)) => {
-            let var = &ns.contracts[*var_contract_no].variables[*var_no];
-            if var.constant {
+    // yul functions cannot access contract symbols
+    if !context.yul_function {
+        return match ns.resolve_var(context.file_no, context.contract_no, id, false) {
+            Some(Symbol::Variable(_, Some(var_contract_no), var_no)) => {
+                let var = &ns.contracts[*var_contract_no].variables[*var_no];
+                if var.immutable {
+                    ns.diagnostics.push(Diagnostic::error(
+                        id.loc,
+                        "assembly access to immutable variables is not supported".to_string(),
+                    ));
+                    return Err(());
+                }
+
+                if var.constant {
+                    Ok(AssemblyExpression::ConstantVariable(
+                        id.loc,
+                        var.ty.clone(),
+                        Some(*var_contract_no),
+                        *var_no,
+                    ))
+                } else {
+                    Ok(AssemblyExpression::StorageVariable(
+                        id.loc,
+                        var.ty.clone(),
+                        *var_contract_no,
+                        *var_no,
+                    ))
+                }
+            }
+            Some(Symbol::Variable(_, None, var_no)) => {
+                let var = &ns.constants[*var_no];
                 Ok(AssemblyExpression::ConstantVariable(
                     id.loc,
                     var.ty.clone(),
-                    Some(*var_contract_no),
+                    None,
                     *var_no,
                 ))
-            } else {
-                Ok(AssemblyExpression::StorageVariable(
+            }
+            None => {
+                ns.diagnostics.push(Diagnostic::error(
                     id.loc,
-                    var.ty.clone(),
-                    *var_contract_no,
-                    *var_no,
-                ))
+                    format!("'{}' is not found", id.name),
+                ));
+                Err(())
             }
-        }
-        Some(Symbol::Variable(_, None, var_no)) => {
-            let var = &ns.constants[*var_no];
-            Ok(AssemblyExpression::ConstantVariable(
-                id.loc,
-                var.ty.clone(),
-                None,
-                *var_no,
-            ))
-        }
-        None => {
-            ns.diagnostics.push(Diagnostic::decl_error(
-                id.loc,
-                format!("'{}' is not found", id.name),
-            ));
-            Err(())
-        }
 
-        _ => {
-            ns.diagnostics.push(Diagnostic::error(
-                id.loc,
-                "only variables can be accessed inside assembly blocks".to_string(),
-            ));
-            Err(())
-        }
+            _ => {
+                ns.diagnostics.push(Diagnostic::error(
+                    id.loc,
+                    "only variables can be accessed inside assembly blocks".to_string(),
+                ));
+                Err(())
+            }
+        };
     }
+
+    ns.diagnostics.push(Diagnostic::error(
+        id.loc,
+        format!("'{}' is not found", id.name),
+    ));
+    Err(())
 }
 
-fn resolve_function_call(
-    functions: &IndexMap<String, AssemblyFunction>,
+pub(crate) fn resolve_function_call(
+    function_table: &FunctionsTable,
     func_call: &AssemblyFunctionCall,
     context: &ExprContext,
     symtable: &Symtable,
@@ -375,9 +393,10 @@ fn resolve_function_call(
     let mut resolved_arguments: Vec<AssemblyExpression> =
         Vec::with_capacity(func_call.arguments.len());
     for item in &func_call.arguments {
-        let resolved_expr = resolve_assembly_expression(item, context, symtable, functions, ns)?;
+        let resolved_expr =
+            resolve_assembly_expression(item, context, symtable, function_table, ns)?;
 
-        if let Some(diagnostic) = check_type(&resolved_expr) {
+        if let Some(diagnostic) = check_type(&resolved_expr, context) {
             ns.diagnostics.push(diagnostic);
             return Err(());
         }
@@ -385,7 +404,7 @@ fn resolve_function_call(
         resolved_arguments.push(resolved_expr);
     }
 
-    if let Some(built_in) = parse_builtin_keyword(&func_call.id.name[..]) {
+    if let Some(built_in) = parse_builtin_keyword(func_call.id.name.as_str()) {
         let prototype = &built_in.get_prototype_info();
         if prototype.no_args as usize != func_call.arguments.len() {
             ns.diagnostics.push(Diagnostic {
@@ -405,7 +424,7 @@ fn resolve_function_call(
 
         let default_builtin_parameter = AssemblyFunctionParameter {
             loc: Loc::Builtin,
-            name: Identifier {
+            id: Identifier {
                 loc: Loc::Builtin,
                 name: "".to_string(),
             },
@@ -413,7 +432,7 @@ fn resolve_function_call(
         };
 
         for item in &resolved_arguments {
-            check_function_argument(&default_builtin_parameter, item, functions, ns);
+            check_function_argument(&default_builtin_parameter, item, function_table, ns);
         }
 
         return Ok(AssemblyExpression::BuiltInCall(
@@ -423,7 +442,7 @@ fn resolve_function_call(
         ));
     }
 
-    if let Some(func) = functions.get(&func_call.id.name) {
+    if let Some(func) = function_table.find(&func_call.id.name) {
         if resolved_arguments.len() != func.params.len() {
             ns.diagnostics.push(Diagnostic::error(
                 func_call.loc,
@@ -438,12 +457,12 @@ fn resolve_function_call(
         }
 
         for (index, item) in func.params.iter().enumerate() {
-            check_function_argument(item, &resolved_arguments[index], functions, ns);
+            check_function_argument(item, &resolved_arguments[index], function_table, ns);
         }
 
         return Ok(AssemblyExpression::FunctionCall(
             func_call.id.loc,
-            func_call.id.name.clone(),
+            func.function_no,
             resolved_arguments,
         ));
     }
@@ -456,67 +475,18 @@ fn resolve_function_call(
     Err(())
 }
 
+/// Check if the provided argument is compatible with the declared parameters of a function.
 fn check_function_argument(
     parameter: &AssemblyFunctionParameter,
     argument: &AssemblyExpression,
-    functions: &IndexMap<String, AssemblyFunction>,
+    function_table: &FunctionsTable,
     ns: &mut Namespace,
 ) {
-    let arg_type = match argument {
-        AssemblyExpression::BoolLiteral(..) => Type::Bool,
-
-        AssemblyExpression::NumberLiteral(_, _, ty)
-        | AssemblyExpression::StringLiteral(_, _, ty)
-        | AssemblyExpression::AssemblyLocalVariable(_, ty, _)
-        | AssemblyExpression::ConstantVariable(_, ty, ..)
-        | AssemblyExpression::SolidityLocalVariable(_, ty, None, _) => ty.clone(),
-
-        AssemblyExpression::SolidityLocalVariable(_, _, Some(_), _)
-        | AssemblyExpression::MemberAccess(..)
-        | AssemblyExpression::StorageVariable(..) => Type::Uint(256),
-
-        AssemblyExpression::BuiltInCall(_, ty, _) => {
-            let prototype = ty.get_prototype_info();
-            if prototype.no_returns == 0 {
-                ns.diagnostics.push(Diagnostic::error(
-                    argument.loc(),
-                    format!("builtin function '{}' returns nothing", prototype.name),
-                ));
-                Type::Void
-            } else if prototype.no_args > 1 {
-                ns.diagnostics.push(Diagnostic::error(
-                    argument.loc(),
-                    format!(
-                        "builtin function '{}' has multiple returns and cannot be used as argument",
-                        prototype.name
-                    ),
-                ));
-                Type::Unreachable
-            } else {
-                Type::Uint(256)
-            }
-        }
-
-        AssemblyExpression::FunctionCall(_, name, ..) => {
-            let func = functions.get(name).unwrap();
-            if func.returns.is_empty() {
-                ns.diagnostics.push(Diagnostic::error(
-                    argument.loc(),
-                    format!("function '{}' returns nothing", func.name),
-                ));
-                Type::Void
-            } else if func.returns.len() > 1 {
-                ns.diagnostics.push(Diagnostic::error(
-                    argument.loc(),
-                    format!(
-                        "function '{}' has multiple returns and cannot be used as argument",
-                        func.name
-                    ),
-                ));
-                Type::Unreachable
-            } else {
-                func.returns[0].ty.clone()
-            }
+    let arg_type = match verify_type_from_expression(argument, function_table) {
+        Ok(ty) => ty,
+        Err(diagnostic) => {
+            ns.diagnostics.push(diagnostic);
+            Type::Unreachable
         }
     };
 
@@ -530,16 +500,16 @@ fn check_function_argument(
     {
         let n1 = parameter.ty.get_type_size();
         let n2 = arg_type.get_type_size();
-        if n1 > n2 {
+        if n1 < n2 {
             ns.diagnostics.push(Diagnostic::warning(
                 argument.loc(),
-                format!("{}-bit type may not fit into '{}'-bit type", n1, n2),
+                format!("{} bit type may not fit into {} bit type", n2, n1),
             ));
         }
     } else if matches!(parameter.ty, Type::Uint(_)) && matches!(arg_type, Type::Int(_)) {
         ns.diagnostics.push(Diagnostic::warning(
             argument.loc(),
-            "Singed integer may not be correctly represented as unsigned integer".to_string(),
+            "signed integer may not be correctly represented as unsigned integer".to_string(),
         ));
     } else if matches!(parameter.ty, Type::Int(_)) && matches!(arg_type, Type::Uint(_)) {
         let n1 = parameter.ty.get_type_size();
@@ -548,7 +518,7 @@ fn check_function_argument(
             ns.diagnostics.push(Diagnostic::warning(
                 argument.loc(),
                 format!(
-                    "{}-bit unsigned integer may not fit into {}-bit signed integer",
+                    "{} bit unsigned integer may not fit into {} bit signed integer",
                     n1, n2
                 ),
             ));
@@ -556,13 +526,14 @@ fn check_function_argument(
     }
 }
 
+/// Resolve variables accessed with suffixes (e.g. 'var.slot', 'var.offset')
 fn resolve_member_access(
     loc: &pt::Loc,
     expr: &pt::AssemblyExpression,
     id: &Identifier,
     context: &ExprContext,
     symtable: &Symtable,
-    functions: &IndexMap<String, AssemblyFunction>,
+    function_table: &FunctionsTable,
     ns: &mut Namespace,
 ) -> Result<AssemblyExpression, ()> {
     let suffix_type = match get_suffix_from_string(&id.name[..]) {
@@ -576,7 +547,7 @@ fn resolve_member_access(
         }
     };
 
-    let resolved_expr = resolve_assembly_expression(expr, context, symtable, functions, ns)?;
+    let resolved_expr = resolve_assembly_expression(expr, context, symtable, function_table, ns)?;
     match resolved_expr {
         AssemblyExpression::ConstantVariable(_, _, Some(_), _) => {
             ns.diagnostics.push(Diagnostic::error(
@@ -598,8 +569,8 @@ fn resolve_member_access(
                     resolved_expr.loc(),
                     "calldata variables only support \".offset\" and \".length\"".to_string(),
                 ));
+                return Err(());
             }
-            return Err(());
         }
 
         AssemblyExpression::SolidityLocalVariable(_, Type::InternalFunction { .. }, ..)
@@ -667,7 +638,68 @@ fn resolve_member_access(
     ))
 }
 
-pub(crate) fn check_type(expr: &AssemblyExpression) -> Option<Diagnostic> {
+/// Check if an assembly expression has been used correctly in a assignment or if the member access
+/// has a valid expression given the context.
+pub(crate) fn check_type(expr: &AssemblyExpression, context: &ExprContext) -> Option<Diagnostic> {
+    if context.lvalue {
+        match expr {
+            AssemblyExpression::SolidityLocalVariable(
+                _,
+                _,
+                Some(StorageLocation::Storage(_)),
+                ..,
+            )
+            | AssemblyExpression::StorageVariable(..) => {
+                return Some(Diagnostic::error(
+                    expr.loc(),
+                    "storage variables cannot be assigned any value in assembly. You may use \"sstore()\"".to_string()
+                ));
+            }
+
+            AssemblyExpression::StringLiteral(..)
+            | AssemblyExpression::NumberLiteral(..)
+            | AssemblyExpression::BoolLiteral(..)
+            | AssemblyExpression::ConstantVariable(..) => {
+                return Some(Diagnostic::error(
+                    expr.loc(),
+                    "cannot assigned a value to a constant".to_string(),
+                ));
+            }
+
+            AssemblyExpression::BuiltInCall(..) | AssemblyExpression::FunctionCall(..) => {
+                return Some(Diagnostic::error(
+                    expr.loc(),
+                    "cannot assign a value to a function".to_string(),
+                ));
+            }
+
+            AssemblyExpression::MemberAccess(_, _, AssemblySuffix::Length) => {
+                return Some(Diagnostic::error(
+                    expr.loc(),
+                    "cannot assign a value to length".to_string(),
+                ));
+            }
+
+            AssemblyExpression::MemberAccess(_, _, AssemblySuffix::Offset) => {
+                return Some(Diagnostic::error(
+                    expr.loc(),
+                    "cannot assign a value to offset".to_string(),
+                ));
+            }
+
+            AssemblyExpression::MemberAccess(_, expr, AssemblySuffix::Slot) => {
+                if matches!(**expr, AssemblyExpression::StorageVariable(..)) {
+                    return Some(Diagnostic::error(
+                        expr.loc(),
+                        "cannot assign to slot of storage variable".to_string(),
+                    ));
+                }
+            }
+
+            _ => (),
+        }
+    }
+
     match expr {
         AssemblyExpression::SolidityLocalVariable(_, _, Some(StorageLocation::Storage(_)), ..)
         | AssemblyExpression::StorageVariable(..) => {

+ 129 - 0
src/sema/assembly/for_loop.rs

@@ -0,0 +1,129 @@
+use crate::ast::Namespace;
+use crate::sema::assembly::block::{process_statements, resolve_assembly_block, AssemblyBlock};
+use crate::sema::assembly::expression::resolve_assembly_expression;
+use crate::sema::assembly::functions::FunctionsTable;
+use crate::sema::assembly::statements::AssemblyStatement;
+use crate::sema::assembly::types::verify_type_from_expression;
+use crate::sema::expression::ExprContext;
+use crate::sema::symtable::{LoopScopes, Symtable};
+use solang_parser::{pt, Diagnostic};
+
+/// Resolve a for-loop statement
+/// Returns the resolved block and a bool to indicate if the next statement is reachable.
+pub(crate) fn resolve_for_loop(
+    assembly_for: &pt::AssemblyFor,
+    context: &ExprContext,
+    mut reachable: bool,
+    loop_scope: &mut LoopScopes,
+    symtable: &mut Symtable,
+    function_table: &mut FunctionsTable,
+    ns: &mut Namespace,
+) -> Result<(AssemblyStatement, bool), ()> {
+    symtable.new_scope();
+    function_table.new_scope();
+
+    let resolved_init_block = resolve_for_init_block(
+        &assembly_for.init_block,
+        context,
+        loop_scope,
+        symtable,
+        function_table,
+        ns,
+    )?;
+    reachable &= resolved_init_block.1;
+
+    let resolved_cond = resolve_assembly_expression(
+        &assembly_for.condition,
+        context,
+        symtable,
+        function_table,
+        ns,
+    )?;
+    match verify_type_from_expression(&resolved_cond, function_table) {
+        Ok(_) => (),
+        Err(diagnostic) => {
+            ns.diagnostics.push(diagnostic);
+            return Err(());
+        }
+    }
+
+    loop_scope.new_scope();
+
+    let resolved_exec_block = resolve_assembly_block(
+        &assembly_for.execution_block.loc,
+        &assembly_for.execution_block.statements,
+        context,
+        reachable,
+        loop_scope,
+        function_table,
+        symtable,
+        ns,
+    );
+    reachable &= resolved_exec_block.1;
+
+    loop_scope.leave_scope();
+
+    let resolved_post_block = resolve_assembly_block(
+        &assembly_for.post_block.loc,
+        &assembly_for.post_block.statements,
+        context,
+        reachable,
+        loop_scope,
+        function_table,
+        symtable,
+        ns,
+    );
+
+    symtable.leave_scope();
+    function_table.leave_scope();
+
+    Ok((
+        AssemblyStatement::For {
+            loc: assembly_for.loc,
+            init_block: resolved_init_block.0,
+            condition: resolved_cond,
+            post_block: resolved_post_block.0,
+            execution_block: resolved_exec_block.0,
+        },
+        resolved_init_block.1,
+    ))
+}
+
+/// Resolve for initialization block.
+/// Returns the resolved block and a bool to indicate if the next statement is reachable.
+fn resolve_for_init_block(
+    init_block: &pt::AssemblyBlock,
+    context: &ExprContext,
+    loop_scope: &mut LoopScopes,
+    symtable: &mut Symtable,
+    function_table: &mut FunctionsTable,
+    ns: &mut Namespace,
+) -> Result<(AssemblyBlock, bool), ()> {
+    for item in &init_block.statements {
+        if matches!(item, pt::AssemblyStatement::FunctionDefinition(_)) {
+            ns.diagnostics.push(Diagnostic::error(
+                item.loc(),
+                "function definitions are not allowed inside for-init block".to_string(),
+            ));
+            return Err(());
+        }
+    }
+
+    let (body, reachable) = process_statements(
+        &init_block.statements,
+        context,
+        true,
+        symtable,
+        loop_scope,
+        function_table,
+        ns,
+    );
+
+    Ok((
+        AssemblyBlock {
+            loc: init_block.loc,
+            body,
+        },
+        reachable,
+    ))
+}

+ 247 - 9
src/sema/assembly/functions.rs

@@ -1,23 +1,261 @@
-use crate::ast::Type;
+use crate::ast::{Namespace, Type};
+use crate::sema::assembly::block::process_statements;
+use crate::sema::assembly::builtin::{assembly_unsupported_builtin, parse_builtin_keyword};
+use crate::sema::assembly::types::get_type_from_string;
 use crate::sema::assembly::AssemblyStatement;
-use crate::sema::symtable::Symtable;
-use indexmap::IndexMap;
-use solang_parser::pt;
+use crate::sema::expression::ExprContext;
+use crate::sema::symtable::{LoopScopes, Symtable, VariableInitializer, VariableUsage};
+use solang_parser::diagnostics::{ErrorType, Level, Note};
+use solang_parser::pt::AssemblyFunctionDefinition;
+use solang_parser::{pt, Diagnostic};
+use std::collections::{HashMap, LinkedList};
+use std::sync::Arc;
 
 #[derive(Debug, Clone)]
 pub struct AssemblyFunction {
     pub loc: pt::Loc,
     pub name: String,
-    pub params: Vec<AssemblyFunctionParameter>,
-    pub returns: Vec<AssemblyFunctionParameter>,
-    pub body: Vec<AssemblyStatement>,
-    pub functions: IndexMap<String, AssemblyFunction>,
+    pub params: Arc<Vec<AssemblyFunctionParameter>>,
+    pub returns: Arc<Vec<AssemblyFunctionParameter>>,
+    pub body: Vec<(AssemblyStatement, bool)>,
     pub symtable: Symtable,
 }
 
 #[derive(Debug, Clone)]
 pub struct AssemblyFunctionParameter {
     pub loc: pt::Loc,
-    pub name: pt::Identifier,
+    pub id: pt::Identifier,
     pub ty: Type,
 }
+
+/// Saves resolved function headers, so that we can account for function calls, before
+/// resolving the function's body
+pub struct FunctionHeader {
+    pub id: pt::Identifier,
+    pub params: Arc<Vec<AssemblyFunctionParameter>>,
+    pub returns: Arc<Vec<AssemblyFunctionParameter>>,
+    pub function_no: usize,
+}
+
+/// Keeps track of declared functions and their scope
+pub struct FunctionsTable {
+    scopes: LinkedList<HashMap<String, usize>>,
+    lookup: Vec<FunctionHeader>,
+    counter: usize,
+    pub resolved_functions: Vec<AssemblyFunction>,
+}
+
+impl FunctionsTable {
+    pub fn new() -> FunctionsTable {
+        FunctionsTable {
+            scopes: LinkedList::new(),
+            lookup: vec![],
+            counter: 0,
+            resolved_functions: vec![],
+        }
+    }
+
+    pub fn new_scope(&mut self) {
+        self.scopes.push_back(HashMap::new());
+    }
+
+    pub fn leave_scope(&mut self) {
+        self.scopes.pop_back();
+    }
+
+    pub fn find(&self, name: &str) -> Option<&FunctionHeader> {
+        for scope in &self.scopes {
+            if let Some(func_idx) = scope.get(name) {
+                return Some(self.lookup.get(*func_idx).unwrap());
+            }
+        }
+        None
+    }
+
+    pub fn get_params_and_returns(
+        &self,
+        name: &str,
+    ) -> (
+        Arc<Vec<AssemblyFunctionParameter>>,
+        Arc<Vec<AssemblyFunctionParameter>>,
+    ) {
+        let header = self.find(name).unwrap();
+        (header.params.clone(), header.returns.clone())
+    }
+
+    pub fn get(&self, index: usize) -> Option<&FunctionHeader> {
+        self.lookup.get(index)
+    }
+
+    pub fn add_function_header(
+        &mut self,
+        id: &pt::Identifier,
+        params: Vec<AssemblyFunctionParameter>,
+        returns: Vec<AssemblyFunctionParameter>,
+    ) -> Option<Diagnostic> {
+        if let Some(func) = self.find(&id.name) {
+            return Some(Diagnostic {
+                level: Level::Error,
+                ty: ErrorType::DeclarationError,
+                pos: id.loc,
+                message: format!("function name '{}' is already taken", id.name),
+                notes: vec![Note {
+                    pos: func.id.loc,
+                    message: "previous declaration found here".to_string(),
+                }],
+            });
+        }
+
+        self.scopes
+            .back_mut()
+            .unwrap()
+            .insert(id.name.clone(), self.counter);
+
+        self.lookup.push(FunctionHeader {
+            id: id.clone(),
+            params: Arc::new(params),
+            returns: Arc::new(returns),
+            function_no: self.counter,
+        });
+        self.counter += 1;
+
+        None
+    }
+}
+
+/// Resolve the parameters of a function declaration
+fn process_parameters(
+    parameters: &[pt::AssemblyTypedIdentifier],
+    ns: &mut Namespace,
+) -> Vec<AssemblyFunctionParameter> {
+    let mut params: Vec<AssemblyFunctionParameter> = Vec::with_capacity(parameters.len());
+    for item in parameters {
+        let ty = match &item.ty {
+            Some(identifier) => {
+                if let Some(solang_type) = get_type_from_string(&identifier.name) {
+                    solang_type
+                } else {
+                    ns.diagnostics.push(Diagnostic::error(
+                        identifier.loc,
+                        format!("unrecognized assembly type: {}", identifier.name),
+                    ));
+
+                    Type::Uint(256)
+                }
+            }
+            None => Type::Uint(256),
+        };
+
+        params.push(AssemblyFunctionParameter {
+            loc: item.loc,
+            id: item.id.clone(),
+            ty,
+        });
+    }
+
+    params
+}
+
+/// Resolve the function header of a declaration and add it to the functions table
+pub(crate) fn process_function_header(
+    func_def: &AssemblyFunctionDefinition,
+    functions_table: &mut FunctionsTable,
+    ns: &mut Namespace,
+) {
+    if let Some(defined_func) = functions_table.find(&func_def.id.name) {
+        ns.diagnostics.push(Diagnostic {
+            level: Level::Error,
+            ty: ErrorType::DeclarationError,
+            pos: func_def.id.loc,
+            message: format!("function '{}' is already defined", func_def.id.name),
+            notes: vec![Note {
+                pos: defined_func.id.loc,
+                message: "found definition here".to_string(),
+            }],
+        });
+        return;
+    } else if parse_builtin_keyword(&func_def.id.name).is_some()
+        || assembly_unsupported_builtin(&func_def.id.name)
+    {
+        ns.diagnostics.push(Diagnostic::error(
+            func_def.loc,
+            format!(
+                "function '{}' is a built-in function and cannot be redefined",
+                func_def.id.name
+            ),
+        ));
+        return;
+    } else if func_def.id.name.starts_with("verbatim") {
+        ns.diagnostics.push(Diagnostic::error(
+            func_def.id.loc,
+            "the prefix 'verbatim' is reserved for verbatim functions".to_string(),
+        ));
+        return;
+    }
+
+    let params = process_parameters(&func_def.params, ns);
+    let returns = process_parameters(&func_def.returns, ns);
+
+    if let Some(diagnostic) = functions_table.add_function_header(&func_def.id, params, returns) {
+        ns.diagnostics.push(diagnostic);
+    }
+}
+
+/// Semantic analysis of function definitions
+pub(crate) fn process_function_definition(
+    func_def: &pt::AssemblyFunctionDefinition,
+    functions_table: &mut FunctionsTable,
+    context: &ExprContext,
+    ns: &mut Namespace,
+) -> Result<AssemblyFunction, ()> {
+    let mut symtable = Symtable::new();
+    let mut local_ctx = context.clone();
+    local_ctx.yul_function = true;
+    functions_table.new_scope();
+
+    let (params, returns) = functions_table.get_params_and_returns(&func_def.id.name);
+
+    for item in &*params {
+        let _ = symtable.exclusive_add(
+            &item.id,
+            item.ty.clone(),
+            ns,
+            VariableInitializer::Assembly(true),
+            VariableUsage::AssemblyLocalVariable,
+            None,
+        );
+    }
+
+    for item in &*returns {
+        let _ = symtable.exclusive_add(
+            &item.id,
+            item.ty.clone(),
+            ns,
+            VariableInitializer::Assembly(false),
+            VariableUsage::AssemblyLocalVariable,
+            None,
+        );
+    }
+
+    let mut loop_scope = LoopScopes::new();
+
+    let (body, _) = process_statements(
+        &func_def.body.statements,
+        &local_ctx,
+        true,
+        &mut symtable,
+        &mut loop_scope,
+        functions_table,
+        ns,
+    );
+
+    functions_table.leave_scope();
+    Ok(AssemblyFunction {
+        loc: func_def.loc,
+        name: func_def.id.name.clone(),
+        params,
+        returns,
+        body,
+        symtable,
+    })
+}

+ 50 - 9
src/sema/assembly/mod.rs

@@ -1,22 +1,63 @@
-use crate::sema::assembly::functions::AssemblyFunction;
-use indexmap::IndexMap;
+use crate::ast::Namespace;
+use crate::sema::assembly::block::process_statements;
+use crate::sema::assembly::functions::{AssemblyFunction, FunctionsTable};
+use crate::sema::assembly::statements::AssemblyStatement;
+use crate::sema::expression::ExprContext;
+use crate::sema::symtable::{LoopScopes, Symtable};
 use solang_parser::pt;
 
+mod block;
 mod builtin;
 mod expression;
+mod for_loop;
 mod functions;
+mod statements;
+mod switch;
 mod tests;
 mod types;
 
-// TODO: Functions can be called anywhere inside the block.
+// TODO: Mark functions and variables as used/unused
+
 #[derive(Debug, Clone)]
-pub struct AssemblyBlock {
+pub struct InlineAssembly {
     pub loc: pt::Loc,
-    pub body: Vec<AssemblyStatement>,
-    pub functions: IndexMap<String, AssemblyFunction>,
+    pub body: Vec<(AssemblyStatement, bool)>,
+    pub functions: Vec<AssemblyFunction>,
 }
 
-#[derive(Debug, Clone)]
-pub enum AssemblyStatement {
-    AssemblyBlock(AssemblyBlock),
+/// Resolves a block of inline assembly
+/// Returns the resolved block and a bool to indicate if the next statement is reachable.
+pub fn resolve_inline_assembly(
+    loc: &pt::Loc,
+    statements: &[pt::AssemblyStatement],
+    context: &ExprContext,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> (InlineAssembly, bool) {
+    let mut functions_table = FunctionsTable::new();
+    functions_table.new_scope();
+    symtable.new_scope();
+    let mut loop_scope = LoopScopes::new();
+
+    let (body, reachable) = process_statements(
+        statements,
+        context,
+        true,
+        symtable,
+        &mut loop_scope,
+        &mut functions_table,
+        ns,
+    );
+
+    symtable.leave_scope();
+    functions_table.leave_scope();
+
+    (
+        InlineAssembly {
+            loc: *loc,
+            body,
+            functions: functions_table.resolved_functions,
+        },
+        reachable,
+    )
 }

+ 446 - 0
src/sema/assembly/statements.rs

@@ -0,0 +1,446 @@
+use crate::ast::Namespace;
+use crate::sema::assembly::block::{resolve_assembly_block, AssemblyBlock};
+use crate::sema::assembly::builtin::{
+    assembly_unsupported_builtin, parse_builtin_keyword, AssemblyBuiltInFunction,
+};
+use crate::sema::assembly::expression::{
+    check_type, resolve_assembly_expression, resolve_function_call, AssemblyExpression,
+};
+use crate::sema::assembly::for_loop::resolve_for_loop;
+use crate::sema::assembly::functions::{process_function_definition, FunctionsTable};
+use crate::sema::assembly::switch::{resolve_condition, resolve_switch, CaseBlock};
+use crate::sema::assembly::types::get_default_type_from_identifier;
+use crate::sema::expression::ExprContext;
+use crate::sema::symtable::{LoopScopes, Symtable, VariableInitializer, VariableUsage};
+use solang_parser::diagnostics::{ErrorType, Level, Note};
+use solang_parser::pt::AssemblyTypedIdentifier;
+use solang_parser::{pt, Diagnostic};
+
+#[derive(Clone, Debug)]
+pub enum AssemblyStatement {
+    FunctionCall(pt::Loc, usize, Vec<AssemblyExpression>),
+    BuiltInCall(pt::Loc, AssemblyBuiltInFunction, Vec<AssemblyExpression>),
+    Block(pt::Loc, Box<AssemblyBlock>),
+    VariableDeclaration(pt::Loc, Vec<usize>, Option<AssemblyExpression>),
+    Assignment(pt::Loc, Vec<AssemblyExpression>, AssemblyExpression),
+    IfBlock(pt::Loc, AssemblyExpression, Box<AssemblyBlock>),
+    Switch {
+        loc: pt::Loc,
+        condition: AssemblyExpression,
+        cases: Vec<CaseBlock>,
+        default: Option<AssemblyBlock>,
+    },
+    For {
+        loc: pt::Loc,
+        init_block: AssemblyBlock,
+        condition: AssemblyExpression,
+        post_block: AssemblyBlock,
+        execution_block: AssemblyBlock,
+    },
+    Leave(pt::Loc),
+    Break(pt::Loc),
+    Continue(pt::Loc),
+}
+
+/// Resolves an assembly statement. Returns a boolean that indicates if the next statement is reachable.
+pub(crate) fn resolve_assembly_statement(
+    statement: &pt::AssemblyStatement,
+    context: &ExprContext,
+    reachable: bool,
+    loop_scope: &mut LoopScopes,
+    symtable: &mut Symtable,
+    resolved_statements: &mut Vec<(AssemblyStatement, bool)>,
+    function_table: &mut FunctionsTable,
+    ns: &mut Namespace,
+) -> Result<bool, ()> {
+    match statement {
+        pt::AssemblyStatement::FunctionDefinition(func_def) => {
+            resolve_function_definition(func_def, context, function_table, ns)?;
+            Ok(true)
+        }
+        pt::AssemblyStatement::FunctionCall(func_call) => {
+            let data =
+                resolve_top_level_function_call(func_call, function_table, context, symtable, ns)?;
+            resolved_statements.push((data.0, reachable));
+            Ok(data.1)
+        }
+
+        pt::AssemblyStatement::Block(block) => {
+            let data = resolve_assembly_block(
+                &block.loc,
+                &block.statements,
+                context,
+                reachable,
+                loop_scope,
+                function_table,
+                symtable,
+                ns,
+            );
+            resolved_statements.push((
+                AssemblyStatement::Block(block.loc, Box::new(data.0)),
+                reachable,
+            ));
+            Ok(data.1)
+        }
+
+        pt::AssemblyStatement::VariableDeclaration(loc, variables, initializer) => {
+            resolved_statements.push((
+                resolve_variable_declaration(
+                    loc,
+                    variables,
+                    initializer,
+                    function_table,
+                    context,
+                    symtable,
+                    ns,
+                )?,
+                reachable,
+            ));
+            Ok(true)
+        }
+
+        pt::AssemblyStatement::Assign(loc, lhs, rhs) => {
+            resolved_statements.push((
+                resolve_assignment(loc, lhs, rhs, context, function_table, symtable, ns)?,
+                reachable,
+            ));
+            Ok(true)
+        }
+
+        pt::AssemblyStatement::If(loc, condition, body) => {
+            resolved_statements.push((
+                resolve_if_block(
+                    loc,
+                    condition,
+                    &body.statements,
+                    context,
+                    reachable,
+                    loop_scope,
+                    function_table,
+                    symtable,
+                    ns,
+                )?,
+                reachable,
+            ));
+            Ok(true)
+        }
+
+        pt::AssemblyStatement::Switch(switch_statement) => {
+            let resolved_switch = resolve_switch(
+                switch_statement,
+                context,
+                reachable,
+                function_table,
+                loop_scope,
+                symtable,
+                ns,
+            )?;
+            resolved_statements.push((resolved_switch.0, reachable));
+            Ok(resolved_switch.1)
+        }
+
+        pt::AssemblyStatement::Break(loc) => {
+            if loop_scope.do_break() {
+                resolved_statements.push((AssemblyStatement::Break(*loc), reachable));
+                Ok(false)
+            } else {
+                ns.diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "break statement outside a for loop".to_string(),
+                ));
+                Err(())
+            }
+        }
+
+        pt::AssemblyStatement::Continue(loc) => {
+            if loop_scope.do_continue() {
+                resolved_statements.push((AssemblyStatement::Continue(*loc), reachable));
+                Ok(false)
+            } else {
+                ns.diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "continue statement outside a for loop".to_string(),
+                ));
+                Err(())
+            }
+        }
+
+        pt::AssemblyStatement::Leave(loc) => {
+            if !context.yul_function {
+                ns.diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "leave statement cannot be used outside a function".to_string(),
+                ));
+                return Err(());
+            }
+            resolved_statements.push((AssemblyStatement::Leave(*loc), reachable));
+            Ok(false)
+        }
+
+        pt::AssemblyStatement::For(for_statement) => {
+            let resolved_for = resolve_for_loop(
+                for_statement,
+                context,
+                reachable,
+                loop_scope,
+                symtable,
+                function_table,
+                ns,
+            )?;
+            resolved_statements.push((resolved_for.0, reachable));
+            Ok(resolved_for.1)
+        }
+    }
+}
+
+pub(crate) fn resolve_function_definition(
+    func_def: &pt::AssemblyFunctionDefinition,
+    context: &ExprContext,
+    function_table: &mut FunctionsTable,
+    ns: &mut Namespace,
+) -> Result<(), ()> {
+    let resolved_func = process_function_definition(func_def, function_table, context, ns)?;
+    function_table.resolved_functions.push(resolved_func);
+    Ok(())
+}
+
+/// Top-leve function calls must not return anything, so there is a special function to handle them.
+fn resolve_top_level_function_call(
+    func_call: &pt::AssemblyFunctionCall,
+    function_table: &mut FunctionsTable,
+    context: &ExprContext,
+    symtable: &Symtable,
+    ns: &mut Namespace,
+) -> Result<(AssemblyStatement, bool), ()> {
+    match resolve_function_call(function_table, func_call, context, symtable, ns) {
+        Ok(AssemblyExpression::BuiltInCall(loc, ty, args)) => {
+            let func_prototype = ty.get_prototype_info();
+            if func_prototype.no_returns != 0 {
+                ns.diagnostics.push(Diagnostic::error(
+                    loc,
+                    "top level function calls must not return anything".to_string(),
+                ));
+                return Err(());
+            }
+            Ok((
+                AssemblyStatement::BuiltInCall(loc, ty, args),
+                !func_prototype.stops_execution,
+            ))
+        }
+        Ok(AssemblyExpression::FunctionCall(loc, function_no, args)) => {
+            let func = function_table.get(function_no).unwrap();
+            if !func.returns.is_empty() {
+                ns.diagnostics.push(Diagnostic::error(
+                    loc,
+                    "top level function calls must not return anything".to_string(),
+                ));
+                return Err(());
+            }
+            Ok((
+                AssemblyStatement::FunctionCall(loc, function_no, args),
+                true,
+            ))
+        }
+
+        Ok(_) => {
+            unreachable!("sema::assembly::resolve_function_call can only return resolved calls")
+        }
+
+        Err(_) => Err(()),
+    }
+}
+
+fn resolve_variable_declaration(
+    loc: &pt::Loc,
+    variables: &[AssemblyTypedIdentifier],
+    initializer: &Option<pt::AssemblyExpression>,
+    function_table: &FunctionsTable,
+    context: &ExprContext,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<AssemblyStatement, ()> {
+    let mut added_variables: Vec<usize> = Vec::with_capacity(variables.len());
+    for item in variables {
+        if let Some(func) = function_table.find(&item.id.name) {
+            ns.diagnostics.push(Diagnostic {
+                level: Level::Error,
+                ty: ErrorType::DeclarationError,
+                pos: item.loc,
+                message: format!("name '{}' has been defined as a function", item.id.name),
+                notes: vec![Note {
+                    pos: func.id.loc,
+                    message: "function defined here".to_string(),
+                }],
+            });
+            return Err(());
+        } else if assembly_unsupported_builtin(&item.id.name)
+            || parse_builtin_keyword(&item.id.name).is_some()
+        {
+            ns.diagnostics.push(Diagnostic::error(
+                item.loc,
+                format!(
+                    "'{}' is a built-in function and cannot be a variable name",
+                    item.id.name
+                ),
+            ));
+            return Err(());
+        } else if item.id.name.starts_with("verbatim") {
+            ns.diagnostics.push(Diagnostic::error(
+                item.loc,
+                "the prefix 'verbatim' is reserved for verbatim functions".to_string(),
+            ));
+            return Err(());
+        }
+
+        let ty = get_default_type_from_identifier(&item.ty, ns)?;
+
+        if let Some(pos) = symtable.exclusive_add(
+            &item.id,
+            ty,
+            ns,
+            VariableInitializer::Assembly(initializer.is_some()),
+            VariableUsage::AssemblyLocalVariable,
+            None,
+        ) {
+            added_variables.push(pos);
+        } else {
+            return Err(());
+        }
+    }
+
+    let resolved_init = if let Some(init_expr) = &initializer {
+        let resolved_expr =
+            resolve_assembly_expression(init_expr, context, symtable, function_table, ns)?;
+        check_assignment_compatibility(loc, variables, &resolved_expr, context, function_table, ns);
+        Some(resolved_expr)
+    } else {
+        None
+    };
+
+    Ok(AssemblyStatement::VariableDeclaration(
+        *loc,
+        added_variables,
+        resolved_init,
+    ))
+}
+
+fn resolve_assignment(
+    loc: &pt::Loc,
+    lhs: &[pt::AssemblyExpression],
+    rhs: &pt::AssemblyExpression,
+    context: &ExprContext,
+    function_table: &FunctionsTable,
+    symtable: &Symtable,
+    ns: &mut Namespace,
+) -> Result<AssemblyStatement, ()> {
+    let mut resolved_lhs: Vec<AssemblyExpression> = Vec::with_capacity(lhs.len());
+    let mut local_ctx = context.clone();
+    local_ctx.lvalue = true;
+    for item in lhs {
+        let resolved = resolve_assembly_expression(item, &local_ctx, symtable, function_table, ns)?;
+        if let Some(diagnostic) = check_type(&resolved, &local_ctx) {
+            ns.diagnostics.push(diagnostic);
+            return Err(());
+        }
+        resolved_lhs.push(resolved);
+    }
+
+    local_ctx.lvalue = false;
+    let resolved_rhs = resolve_assembly_expression(rhs, &local_ctx, symtable, function_table, ns)?;
+    check_assignment_compatibility(
+        loc,
+        &resolved_lhs,
+        &resolved_rhs,
+        context,
+        function_table,
+        ns,
+    );
+
+    Ok(AssemblyStatement::Assignment(
+        *loc,
+        resolved_lhs,
+        resolved_rhs,
+    ))
+}
+
+/// Checks the the left hand side of an assignment is compatible with it right hand side
+fn check_assignment_compatibility<T>(
+    loc: &pt::Loc,
+    lhs: &[T],
+    rhs: &AssemblyExpression,
+    context: &ExprContext,
+    function_table: &FunctionsTable,
+    ns: &mut Namespace,
+) {
+    match rhs {
+        AssemblyExpression::FunctionCall(_, function_no, ..) => {
+            let func = function_table.get(*function_no).unwrap();
+            if func.returns.len() != lhs.len() {
+                ns.diagnostics.push(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "{} variables on the left hand side, but the function returns {} values",
+                        lhs.len(),
+                        func.returns.len()
+                    ),
+                ));
+            }
+        }
+
+        AssemblyExpression::BuiltInCall(_, ty, _) => {
+            let prototype = ty.get_prototype_info();
+            if prototype.no_returns as usize != lhs.len() {
+                ns.diagnostics.push(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "{} variables on the left hand side, but the function returns {} values",
+                        lhs.len(),
+                        prototype.no_returns
+                    ),
+                ));
+            }
+        }
+
+        _ => {
+            if lhs.len() != 1 {
+                ns.diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "a single value cannot be assigned to multiple variables".to_string(),
+                ));
+            } else if let Some(diagnostic) = check_type(rhs, context) {
+                ns.diagnostics.push(diagnostic);
+            }
+        }
+    }
+}
+
+fn resolve_if_block(
+    loc: &pt::Loc,
+    condition: &pt::AssemblyExpression,
+    if_block: &[pt::AssemblyStatement],
+    context: &ExprContext,
+    reachable: bool,
+    loop_scope: &mut LoopScopes,
+    function_table: &mut FunctionsTable,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<AssemblyStatement, ()> {
+    let resolved_condition = resolve_condition(condition, context, symtable, function_table, ns)?;
+
+    let resolved_block = resolve_assembly_block(
+        loc,
+        if_block,
+        context,
+        reachable,
+        loop_scope,
+        function_table,
+        symtable,
+        ns,
+    );
+
+    Ok(AssemblyStatement::IfBlock(
+        *loc,
+        resolved_condition,
+        Box::new(resolved_block.0),
+    ))
+}

+ 211 - 0
src/sema/assembly/switch.rs

@@ -0,0 +1,211 @@
+use crate::ast::Namespace;
+use crate::sema::assembly::block::{resolve_assembly_block, AssemblyBlock};
+use crate::sema::assembly::expression::{
+    check_type, resolve_assembly_expression, AssemblyExpression,
+};
+use crate::sema::assembly::functions::FunctionsTable;
+use crate::sema::assembly::statements::AssemblyStatement;
+use crate::sema::assembly::types::verify_type_from_expression;
+use crate::sema::expression::ExprContext;
+use crate::sema::symtable::{LoopScopes, Symtable};
+use solang_parser::pt::{AssemblySwitchOptions, CodeLocation};
+use solang_parser::{pt, Diagnostic};
+
+#[derive(Debug, Clone)]
+pub struct CaseBlock {
+    pub condition: AssemblyExpression,
+    pub block: AssemblyBlock,
+}
+
+/// Resolve switch statement
+/// Returns the resolved block and a bool to indicate if the next statement is reachable.
+pub(crate) fn resolve_switch(
+    assembly_switch: &pt::AssemblySwitch,
+    context: &ExprContext,
+    mut reachable: bool,
+    function_table: &mut FunctionsTable,
+    loop_scope: &mut LoopScopes,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<(AssemblyStatement, bool), ()> {
+    let resolved_condition = resolve_condition(
+        &assembly_switch.condition,
+        context,
+        symtable,
+        function_table,
+        ns,
+    )?;
+    let mut default_block: Option<AssemblyBlock> = None;
+    let mut case_blocks: Vec<CaseBlock> = Vec::with_capacity(assembly_switch.cases.len());
+    for item in &assembly_switch.cases {
+        let block_reachable = resolve_case_or_default(
+            item,
+            &mut default_block,
+            &mut case_blocks,
+            context,
+            reachable,
+            function_table,
+            loop_scope,
+            symtable,
+            ns,
+        )?;
+        reachable |= block_reachable;
+    }
+
+    if assembly_switch.default.is_some() && default_block.is_some() {
+        ns.diagnostics.push(Diagnostic::error(
+            assembly_switch.default.as_ref().unwrap().loc(),
+            "Only one default block is allowed".to_string(),
+        ));
+        return Err(());
+    } else if let Some(default_unwrapped) = &assembly_switch.default {
+        let block_reachable = resolve_case_or_default(
+            default_unwrapped,
+            &mut default_block,
+            &mut case_blocks,
+            context,
+            reachable,
+            function_table,
+            loop_scope,
+            symtable,
+            ns,
+        )?;
+        reachable |= block_reachable;
+    } else if assembly_switch.default.is_none() && default_block.is_none() {
+        reachable |= true;
+    }
+
+    Ok((
+        AssemblyStatement::Switch {
+            loc: assembly_switch.loc,
+            condition: resolved_condition,
+            cases: case_blocks,
+            default: default_block,
+        },
+        reachable,
+    ))
+}
+
+/// Resolves condition statements for either if-statement and switch-statements
+pub(crate) fn resolve_condition(
+    condition: &pt::AssemblyExpression,
+    context: &ExprContext,
+    symtable: &Symtable,
+    function_table: &FunctionsTable,
+    ns: &mut Namespace,
+) -> Result<AssemblyExpression, ()> {
+    let resolved_condition =
+        resolve_assembly_expression(condition, context, symtable, function_table, ns)?;
+    if let Err(diagnostic) = verify_type_from_expression(&resolved_condition, function_table) {
+        ns.diagnostics.push(diagnostic);
+        return Err(());
+    } else if let Some(diagnostic) = check_type(&resolved_condition, context) {
+        ns.diagnostics.push(diagnostic);
+        return Err(());
+    }
+
+    Ok(resolved_condition)
+}
+
+/// Resolve case or default from a switch statements
+fn resolve_case_or_default(
+    switch_case: &pt::AssemblySwitchOptions,
+    default_block: &mut Option<AssemblyBlock>,
+    case_blocks: &mut Vec<CaseBlock>,
+    context: &ExprContext,
+    reachable: bool,
+    function_table: &mut FunctionsTable,
+    loop_scope: &mut LoopScopes,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<bool, ()> {
+    match switch_case {
+        AssemblySwitchOptions::Case(loc, expr, block) => {
+            let resolved_case = resolve_case_block(
+                loc,
+                default_block.is_some(),
+                expr,
+                &block.statements,
+                context,
+                reachable,
+                function_table,
+                loop_scope,
+                symtable,
+                ns,
+            )?;
+            case_blocks.push(resolved_case.0);
+            Ok(resolved_case.1)
+        }
+
+        AssemblySwitchOptions::Default(loc, block) => {
+            let resolved_default = resolve_assembly_block(
+                loc,
+                &block.statements,
+                context,
+                reachable,
+                loop_scope,
+                function_table,
+                symtable,
+                ns,
+            );
+            *default_block = Some(resolved_default.0);
+            Ok(resolved_default.1)
+        }
+    }
+}
+
+/// Resolve a case from a switch statement
+fn resolve_case_block(
+    loc: &pt::Loc,
+    has_default: bool,
+    condition: &pt::AssemblyExpression,
+    block: &[pt::AssemblyStatement],
+    context: &ExprContext,
+    reachable: bool,
+    function_table: &mut FunctionsTable,
+    loop_scope: &mut LoopScopes,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<(CaseBlock, bool), ()> {
+    if has_default {
+        ns.diagnostics.push(Diagnostic::error(
+            *loc,
+            "A case-block cannot come after a default block".to_string(),
+        ));
+        return Err(());
+    }
+    let resolved_condition =
+        resolve_assembly_expression(condition, context, symtable, function_table, ns)?;
+    match resolved_condition {
+        AssemblyExpression::NumberLiteral(..)
+        | AssemblyExpression::StringLiteral(..)
+        | AssemblyExpression::BoolLiteral(..) => (),
+
+        _ => {
+            ns.diagnostics.push(Diagnostic::error(
+                resolved_condition.loc(),
+                "'case' can only be followed by a literal".to_string(),
+            ));
+            return Err(());
+        }
+    }
+
+    let case_block = resolve_assembly_block(
+        loc,
+        block,
+        context,
+        reachable,
+        loop_scope,
+        function_table,
+        symtable,
+        ns,
+    );
+
+    Ok((
+        CaseBlock {
+            condition: resolved_condition,
+            block: case_block.0,
+        },
+        case_block.1,
+    ))
+}

+ 274 - 0
src/sema/assembly/tests/block.rs

@@ -0,0 +1,274 @@
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
+
+#[test]
+fn unreachable_leave() {
+    let file = r#"
+    contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                function tryThis(a, b) {
+                    a := add(a, 4)
+                    leave
+                    b := add(b, 6)
+                    let c := mul(a, b)
+                }
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                function tryThis(a, b) {
+                    a := add(a, 4)
+                    if gt(a, 5) {
+                        leave
+                    }
+                    b := add(b, 6)
+                    let c := mul(a, b)
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}
+
+#[test]
+fn unreachable_continue() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let a := 0
+                for {let i := 0} lt(i, 10) {i := add(i, 1)} {
+                    a := shr(a, 2)
+                    if lt(a, 4) {
+                        continue
+                        let b := shr(6, 5)
+                        a := mul(a, b)
+                    }
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let a := 0
+                for {let i := 0} lt(i, 10) {i := add(i, 1)} {
+                    a := shr(a, 2)
+                    if lt(a, 4) {
+                        continue
+                    }
+                    let b := shr(6, 5)
+                        a := mul(a, b)
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}
+
+#[test]
+fn unreachable_break() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let a := 0
+                for {let i := 0} lt(i, 10) {i := add(i, 1)} {
+                    a := shr(a, 2)
+                    if lt(a, 4) {
+                        break
+                        let b := shr(6, 5)
+                        a := mul(a, b)
+                    }
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let a := 0
+                for {let i := 0} lt(i, 10) {i := add(i, 1)} {
+                    a := shr(a, 2)
+                    if lt(a, 4) {
+                        break
+                    }
+                    let b := shr(6, 5)
+                        a := mul(a, b)
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}
+
+#[test]
+fn unreachable_switch() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public {
+        assembly {
+            {
+                let a := 0
+                for {let i := 11} lt(i, 10) {i := add(i, 1)} {
+                    a := shr(a, 2)
+                    if lt(a, 4) {
+                        continue
+                    }
+                    let b := shr(6, 5)
+                        a := mul(a, b)
+                    stop()
+                }
+
+                switch a
+                case 0 {stop()}
+                case 1 {return(0, 0)}
+                case 2 {revert(2, 3)}
+                case 3 {selfdestruct(0x40)}
+                default {invalid()}
+
+                let b := shr(a, 1)
+                return(b, 2)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public {
+        assembly {
+            {
+                let a := 0
+
+                switch a
+                case 0 {stop()}
+                case 1 {return(0, 0)}
+                case 2 {revert(2, 3)}
+                case 3 {selfdestruct(0x40)}
+                default {a := add(a, 3)}
+
+                let b := shr(a, 1)
+                return(b, 2)
+            }
+        }
+    }
+}    "#;
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    function testAsm() public {
+        assembly {
+            {
+                let a := 0
+
+                switch a
+                case 0 {stop()}
+                case 1 {return(0, 0)}
+                case 2 {revert(2, 3)}
+                case 3 {selfdestruct(0x40)}
+
+                let b := shr(a, 1)
+                return(b, 2)
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}

+ 601 - 95
src/sema/assembly/tests/expression.rs

@@ -5,11 +5,11 @@ use crate::sema::assembly::builtin::AssemblyBuiltInFunction;
 use crate::sema::assembly::expression::{
     check_type, resolve_assembly_expression, AssemblyExpression, AssemblySuffix,
 };
-use crate::sema::assembly::functions::{AssemblyFunction, AssemblyFunctionParameter};
+use crate::sema::assembly::functions::{AssemblyFunctionParameter, FunctionsTable};
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
 use crate::sema::expression::ExprContext;
-use crate::sema::symtable::{Symtable, VariableUsage};
+use crate::sema::symtable::{Symtable, VariableInitializer, VariableUsage};
 use crate::{ast, Target};
-use indexmap::IndexMap;
 use num_bigint::BigInt;
 use num_traits::FromPrimitive;
 use solang_parser::pt;
@@ -27,9 +27,10 @@ fn resolve_bool_literal() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
 
     let mut ns = Namespace::new(Target::Solana);
     let expr = pt::AssemblyExpression::BoolLiteral(
@@ -41,7 +42,8 @@ fn resolve_bool_literal() {
         }),
     );
 
-    let resolved_type = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved_type =
+        resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved_type.is_ok());
     assert!(ns.diagnostics.is_empty());
     let unwrapped = resolved_type.unwrap();
@@ -52,7 +54,8 @@ fn resolve_bool_literal() {
     );
 
     let expr = pt::AssemblyExpression::BoolLiteral(Loc::File(0, 3, 5), true, None);
-    let resolved_type = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved_type =
+        resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
 
     assert!(resolved_type.is_ok());
     assert!(ns.diagnostics.is_empty());
@@ -72,9 +75,10 @@ fn resolve_number_literal() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
 
     let loc = Loc::File(0, 3, 5);
     let mut ns = Namespace::new(Target::Solana);
@@ -86,7 +90,7 @@ fn resolve_number_literal() {
             name: "u64".to_string(),
         }),
     );
-    let parsed = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let parsed = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(parsed.is_ok());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -103,17 +107,17 @@ fn resolve_number_literal() {
             name: "u128".to_string(),
         }),
     );
-    let parsed = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let parsed = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(parsed.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
         ns.diagnostics[0].message,
-        "singed value cannot fit in unsigned type"
+        "signed integer cannot fit in unsigned integer"
     );
 
     ns.diagnostics.clear();
     let expr = pt::AssemblyExpression::NumberLiteral(loc, BigInt::from(20), None);
-    let parsed = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let parsed = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(parsed.is_ok());
     assert!(ns.diagnostics.is_empty());
     assert_eq!(
@@ -131,9 +135,10 @@ fn resolve_hex_number_literal() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
 
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(0, 3, 5);
@@ -146,7 +151,7 @@ fn resolve_hex_number_literal() {
         }),
     );
 
-    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved.is_ok());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -163,7 +168,7 @@ fn resolve_hex_number_literal() {
             name: "s64".to_string(),
         }),
     );
-    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved.is_ok());
     assert!(ns.diagnostics.is_empty());
     assert_eq!(
@@ -181,9 +186,10 @@ fn resolve_hex_string_literal() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
 
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(0, 3, 5);
@@ -195,7 +201,7 @@ fn resolve_hex_string_literal() {
         None,
     );
 
-    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -214,7 +220,7 @@ fn resolve_hex_string_literal() {
             name: "myType".to_string(),
         }),
     );
-    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -233,7 +239,7 @@ fn resolve_hex_string_literal() {
             name: "u256".to_string(),
         }),
     );
-    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved.is_ok());
     assert!(ns.diagnostics.is_empty());
     assert_eq!(
@@ -251,9 +257,10 @@ fn resolve_string_literal() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
 
     let mut ns = Namespace::new(Target::Solana);
     let loc = Loc::File(0, 3, 5);
@@ -268,7 +275,7 @@ fn resolve_string_literal() {
         }),
     );
 
-    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &functions, &mut ns);
+    let resolved = resolve_assembly_expression(&expr, &ctx, &symtable, &function_table, &mut ns);
     assert!(resolved.is_ok());
     assert!(ns.diagnostics.is_empty());
     assert_eq!(
@@ -290,9 +297,10 @@ fn resolve_variable_local() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let mut symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(1, 2, 3);
 
@@ -304,7 +312,7 @@ fn resolve_variable_local() {
             },
             Type::Uint(32),
             &mut ns,
-            None,
+            VariableInitializer::Assembly(false),
             VariableUsage::AssemblyLocalVariable,
             None,
         )
@@ -317,7 +325,7 @@ fn resolve_variable_local() {
             },
             Type::Uint(32),
             &mut ns,
-            None,
+            VariableInitializer::Assembly(false),
             VariableUsage::LocalVariable,
             None,
         )
@@ -335,8 +343,8 @@ fn resolve_variable_local() {
     let expected_1 = AssemblyExpression::AssemblyLocalVariable(loc, Type::Uint(32), pos1);
     let expected_2 = AssemblyExpression::SolidityLocalVariable(loc, Type::Uint(32), None, pos2);
 
-    let res1 = resolve_assembly_expression(&expr1, &context, &symtable, &functions, &mut ns);
-    let res2 = resolve_assembly_expression(&expr2, &context, &symtable, &functions, &mut ns);
+    let res1 = resolve_assembly_expression(&expr1, &context, &symtable, &function_table, &mut ns);
+    let res2 = resolve_assembly_expression(&expr2, &context, &symtable, &function_table, &mut ns);
 
     assert!(res1.is_ok());
     assert!(res2.is_ok());
@@ -355,9 +363,10 @@ fn resolve_variable_contract() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(0, 2, 3);
     let mut contract = ast::Contract::new("test", ContractTy::Contract(loc), vec![], loc);
@@ -386,6 +395,19 @@ fn resolve_variable_contract() {
         read: false,
     });
 
+    contract.variables.push(Variable {
+        tags: vec![],
+        name: "imut".to_string(),
+        loc,
+        ty: Type::Int(128),
+        visibility: Visibility::Public(None),
+        constant: false,
+        immutable: true,
+        initializer: None,
+        assigned: false,
+        read: false,
+    });
+
     ns.contracts.push(contract);
 
     ns.constants.push(Variable {
@@ -409,6 +431,10 @@ fn resolve_variable_contract() {
         (0, Some(0), "var2".to_string()),
         Symbol::Variable(loc, Some(0), 1),
     );
+    ns.variable_symbols.insert(
+        (0, Some(0), "imut".to_string()),
+        Symbol::Variable(loc, Some(0), 2),
+    );
     ns.variable_symbols.insert(
         (0, None, "var3".to_string()),
         Symbol::Variable(loc, None, 0),
@@ -420,7 +446,7 @@ fn resolve_variable_contract() {
         loc,
         name: "var1".to_string(),
     });
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_ok());
     assert_eq!(
         AssemblyExpression::ConstantVariable(loc, Type::Bool, Some(0), 0),
@@ -431,7 +457,7 @@ fn resolve_variable_contract() {
         loc,
         name: "var2".to_string(),
     });
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_ok());
     assert_eq!(
         AssemblyExpression::StorageVariable(loc, Type::Int(128), 0, 1),
@@ -442,7 +468,7 @@ fn resolve_variable_contract() {
         loc,
         name: "var3".to_string(),
     });
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_ok());
     assert_eq!(
         AssemblyExpression::ConstantVariable(loc, Type::Uint(32), None, 0),
@@ -453,7 +479,7 @@ fn resolve_variable_contract() {
         loc,
         name: "func".to_string(),
     });
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -466,10 +492,23 @@ fn resolve_variable_contract() {
         loc,
         name: "none".to_string(),
     });
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(ns.diagnostics[0].message, "'none' is not found");
+
+    ns.diagnostics.clear();
+    let expr = pt::AssemblyExpression::Variable(Identifier {
+        loc,
+        name: "imut".to_string(),
+    });
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
+    assert!(res.is_err());
+    assert_eq!(ns.diagnostics.len(), 1);
+    assert_eq!(
+        ns.diagnostics[0].message,
+        "assembly access to immutable variables is not supported"
+    );
 }
 
 #[test]
@@ -481,9 +520,11 @@ fn function_call() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let mut functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let mut function_table = FunctionsTable::new();
+    function_table.new_scope();
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(0, 2, 3);
 
@@ -495,7 +536,7 @@ fn function_call() {
         },
         arguments: vec![],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -512,7 +553,7 @@ fn function_call() {
         },
         arguments: vec![],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -538,7 +579,7 @@ fn function_call() {
         },
         arguments: vec![arg.clone()],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -555,31 +596,27 @@ fn function_call() {
         },
         arguments: vec![arg.clone()],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_ok());
     assert_eq!(
         AssemblyExpression::BuiltInCall(
             loc,
             AssemblyBuiltInFunction::Not,
             vec![
-                resolve_assembly_expression(&arg, &context, &symtable, &functions, &mut ns)
+                resolve_assembly_expression(&arg, &context, &symtable, &function_table, &mut ns)
                     .unwrap()
             ]
         ),
         res.unwrap()
     );
 
-    functions.insert(
-        "myFunc".to_string(),
-        AssemblyFunction {
+    function_table.add_function_header(
+        &Identifier {
             loc,
             name: "myFunc".to_string(),
-            params: vec![],
-            returns: vec![],
-            body: vec![],
-            functions: IndexMap::new(),
-            symtable: Symtable::new(),
         },
+        vec![],
+        vec![],
     );
 
     let expr = pt::AssemblyExpression::FunctionCall(Box::new(AssemblyFunctionCall {
@@ -590,7 +627,7 @@ fn function_call() {
         },
         arguments: vec![arg.clone()],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -607,10 +644,10 @@ fn function_call() {
         },
         arguments: vec![],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_ok());
     assert_eq!(
-        AssemblyExpression::FunctionCall(loc, "myFunc".to_string(), vec![]),
+        AssemblyExpression::FunctionCall(loc, 0, vec![]),
         res.unwrap()
     );
 
@@ -622,7 +659,7 @@ fn function_call() {
         },
         arguments: vec![],
     }));
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(ns.diagnostics[0].message, "function 'none' is not defined");
@@ -637,53 +674,47 @@ fn check_arguments() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let mut functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let mut function_table = FunctionsTable::new();
+    function_table.new_scope();
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(0, 2, 3);
 
-    functions.insert(
-        "func1".to_string(),
-        AssemblyFunction {
+    function_table.add_function_header(
+        &Identifier {
             loc,
             name: "func1".to_string(),
-            params: vec![],
-            returns: vec![],
-            body: vec![],
-            functions: IndexMap::new(),
-            symtable: Symtable::new(),
         },
+        vec![],
+        vec![],
     );
 
-    functions.insert(
-        "func2".to_string(),
-        AssemblyFunction {
+    function_table.add_function_header(
+        &Identifier {
             loc,
             name: "func2".to_string(),
-            params: vec![],
-            returns: vec![
-                AssemblyFunctionParameter {
+        },
+        vec![],
+        vec![
+            AssemblyFunctionParameter {
+                loc,
+                id: Identifier {
                     loc,
-                    name: Identifier {
-                        loc,
-                        name: "ret1".to_string(),
-                    },
-                    ty: Type::Uint(256),
+                    name: "ret1".to_string(),
                 },
-                AssemblyFunctionParameter {
+                ty: Type::Uint(256),
+            },
+            AssemblyFunctionParameter {
+                loc,
+                id: Identifier {
                     loc,
-                    name: Identifier {
-                        loc,
-                        name: "ret2".to_string(),
-                    },
-                    ty: Type::Uint(256),
+                    name: "ret2".to_string(),
                 },
-            ],
-            body: vec![],
-            functions: IndexMap::new(),
-            symtable: Symtable::new(),
-        },
+                ty: Type::Uint(256),
+            },
+        ],
     );
 
     let expr = pt::AssemblyExpression::FunctionCall(Box::new(AssemblyFunctionCall {
@@ -708,7 +739,7 @@ fn check_arguments() {
         ))],
     }));
 
-    let _ = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let _ = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(!ns.diagnostics.is_empty());
     assert_eq!(
         ns.diagnostics[0].message,
@@ -734,7 +765,7 @@ fn check_arguments() {
         ))],
     }));
 
-    let _ = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let _ = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(!ns.diagnostics.is_empty());
     assert_eq!(
         ns.diagnostics[0].message,
@@ -760,11 +791,11 @@ fn check_arguments() {
         ))],
     }));
 
-    let _ = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let _ = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(!ns.diagnostics.is_empty());
     assert_eq!(
         ns.diagnostics[0].message,
-        "function 'func2' has multiple returns and cannot be used as argument"
+        "function 'func2' has multiple returns and cannot be used in this scope"
     );
 }
 
@@ -777,9 +808,10 @@ fn test_member_access() {
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
     let symtable = Symtable::new();
-    let functions: IndexMap<String, AssemblyFunction> = IndexMap::new();
+    let function_table = FunctionsTable::new();
     let mut ns = Namespace::new(Target::Ewasm);
     let loc = Loc::File(0, 2, 3);
 
@@ -813,7 +845,7 @@ fn test_member_access() {
         },
     );
 
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -831,7 +863,7 @@ fn test_member_access() {
         },
     );
 
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_err());
     assert_eq!(ns.diagnostics.len(), 1);
     assert_eq!(
@@ -840,9 +872,6 @@ fn test_member_access() {
     );
     ns.diagnostics.clear();
 
-    // TODO: tests with member access are difficult due to nested expressions.
-    // Such tests must be done when Yul sema is complete.
-
     let expr = pt::AssemblyExpression::Member(
         loc,
         Box::new(pt::AssemblyExpression::Variable(Identifier {
@@ -855,7 +884,7 @@ fn test_member_access() {
         },
     );
 
-    let res = resolve_assembly_expression(&expr, &context, &symtable, &functions, &mut ns);
+    let res = resolve_assembly_expression(&expr, &context, &symtable, &function_table, &mut ns);
     assert!(res.is_ok());
     assert!(ns.diagnostics.is_empty());
     assert_eq!(
@@ -878,7 +907,17 @@ fn test_check_types() {
         0,
     );
 
-    let res = check_type(&expr);
+    let context = ExprContext {
+        file_no: 0,
+        contract_no: Some(0),
+        function_no: Some(0),
+        unchecked: false,
+        constant: false,
+        lvalue: false,
+        yul_function: false,
+    };
+
+    let res = check_type(&expr, &context);
     assert!(res.is_some());
     assert_eq!(
         res.unwrap().message,
@@ -886,7 +925,7 @@ fn test_check_types() {
     );
 
     let expr = AssemblyExpression::StorageVariable(loc, Type::Int(16), 0, 1);
-    let res = check_type(&expr);
+    let res = check_type(&expr, &context);
     assert!(res.is_some());
     assert_eq!(
         res.unwrap().message,
@@ -899,11 +938,478 @@ fn test_check_types() {
         Some(StorageLocation::Calldata(loc)),
         2,
     );
-    let res = check_type(&expr);
+    let res = check_type(&expr, &context);
     assert!(res.is_some());
     assert_eq!(res.unwrap().message, "Calldata arrays must be accessed with \".offset\", \".length\" and the \"calldatacopy\" function");
 
     let expr = AssemblyExpression::StringLiteral(loc, vec![0, 255, 20], Type::Uint(256));
-    let res = check_type(&expr);
+    let res = check_type(&expr, &context);
     assert!(res.is_none());
 }
+
+#[test]
+fn test_check_types_resolved() {
+    let file = r#"
+contract testTypes {
+    uint256 b;
+    function testAsm() public pure {
+        assembly {
+            {
+                let x := 0
+                b.offset := add(x, 2)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "cannot assign a value to offset"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    uint256 b;
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+                let x := 0
+                vl.length := add(x, 2)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "cannot assign a value to length"
+    ));
+
+    let file = r#"
+contract testTypes {
+    uint256 b;
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+                let x := 0
+                pop(x) := add(x, 2)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"unrecognised token `:=', expected ")", ",", "address", "bool", "break", "case", "continue", "default", "for", "function", "if", "leave", "let", "return", "revert", "switch", "{", "}", identifier"#
+    ));
+
+    let file = r#"
+uint256 constant b = 1;
+contract testTypes {
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+                let x := 0
+                b := add(x, 2)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "cannot assigned a value to a constant"
+    ));
+
+    let file = r#"
+contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+
+    test tt1;
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        assembly {
+            {
+                let x := 0
+                tt1.slot := add(x, 2)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "cannot assign to slot of storage variable"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+
+    test tt1;
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        assembly {
+            {
+                let x := 0
+                tt2.slot := add(x, 2)
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 3);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "function parameter 'vl' has never been read"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+
+    test tt1;
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        assembly {
+            {
+                let x := 0
+                tt2 := add(x, 2)
+            }
+        }
+    }
+}   "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"storage variables cannot be assigned any value in assembly. You may use "sstore()""#
+    ));
+
+    let file = r#"
+contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+
+    test tt1;
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        assembly {
+            {
+                let x := 0
+                let y := add(tt2, 2)
+            }
+        }
+    }
+}    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"Storage variables must be accessed with ".slot" or ".offset""#
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+                let x := 4
+                let y := add(vl, x)
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"Calldata arrays must be accessed with ".offset", ".length" and the "calldatacopy" function"#
+    ));
+}
+
+#[test]
+fn test_member_access_resolved() {
+    let file = r#"
+contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+    test tt1;
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        assembly {
+            {
+                let x := tt1.slot.length
+               // let y := tt2.length
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "there cannot be multiple suffixes to a name"
+    ));
+
+    let file = r#"
+contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+    test tt1;
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        assembly {
+            {
+                //let x := tt1.slot.length
+               let y := tt2.length
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"state variables only support ".slot" and ".offset""#
+    ));
+
+    let file = r#"
+contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+    test tt1;
+    function bar(uint a) public pure returns (uint) {
+        return a + 3;
+    }
+
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        function(uint) internal pure returns (uint) func = bar;
+        assembly {
+            {
+               let y := func.length
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "only variables of type external function pointer support suffixes"
+    ));
+
+    let file = r#"
+contract testTypes {
+    struct test {
+        uint a;
+        uint b;
+    }
+    test tt1;
+    function bar(uint a) public pure returns (uint) {
+        return a + 3;
+    }
+
+    function testAsm(uint[] calldata vl) public pure {
+        test storage tt2 = tt1;
+        function(uint) external pure returns (uint) func = this.bar;
+        assembly {
+            {
+               let y := func.length
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"variables of type function pointer only support ".selector" and ".address" suffixes"#
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+               let y := vl.selector
+            }
+        }
+    }
+}    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"calldata variables only support ".offset" and ".length""#
+    ));
+
+    let file = r#"
+contract testTypes {
+    uint constant cte = 5;
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+               let y := cte.offset
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "the suffixes .offset and .slot can only be used in non-constant storage variables"
+    ));
+
+    let file = r#"
+contract testTypes {
+    uint constant cte = 5;
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+               let y := cte.dada
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "the provided suffix is not allowed in yul"
+    ));
+}
+
+#[test]
+fn test_check_argument() {
+    let file = r#"
+contract testTypes {
+    function testAsm(uint[] calldata vl) public pure {
+        assembly {
+            {
+                let a1 : u32 := 5
+                let b1 : s32 := 6
+                let c1 : u256 := 7
+                let d1 : s256 := 8
+                let f1 : u256 := 9
+
+                let ret1 := testPars(a1, b1, c1, d1, f1)
+                function testPars(a : s32, b:u32, c:u128, d:s128, f : bool) -> ret{
+                    ret := add(a, b)
+                    ret := add(c, ret)
+                    ret := add(d, ret)
+                    if f {
+                        ret := 0
+                    }
+                }
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "32 bit unsigned integer may not fit into 32 bit signed integer"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "signed integer may not be correctly represented as unsigned integer"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "256 bit type may not fit into 128 bit type"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "256 bit type may not fit into 128 bit type"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "Truncating argument to bool"
+    ));
+}
+
+#[test]
+fn address_member_access() {
+    let file = r#"
+contract C {
+    // Assigns a new selector and address to the return variable @fun
+    function combineToFunctionPointer(address newAddress, uint newSelector) public pure returns (function() external fun) {
+        assembly {
+            fun.selector := newSelector
+            fun.address  := newAddress
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 5);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "function parameter 'newAddress' has never been read"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "function parameter 'newSelector' has never been read"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "return variable 'fun' has never been assigned"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘C’"
+    ));
+}

+ 153 - 0
src/sema/assembly/tests/for_loop.rs

@@ -0,0 +1,153 @@
+#![cfg(test)]
+
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
+
+#[test]
+fn function_inside_init() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let x := 0
+                for {
+                    let i := 0
+                    function tryThis(a, v) -> ret : u256 {
+                        ret := mulmod(a, 2, v)
+                    }
+                } lt(i, 0x100) {
+                    i := add(i, 0x20)
+                } {
+                    x := add(x, mload(i))
+                    if lt(x, 30) {
+                        continue
+                    }
+                    x := add(x, 1)
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "function definitions are not allowed inside for-init block"
+    ));
+}
+
+#[test]
+fn unreachable_execution() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public {
+        assembly {
+
+            let a := 0
+            stop()
+            for {let i := 11
+            } lt(i, 10) {i := add(i, 1)
+        } {
+                a := shr(i, 2)
+                let b := shr(6, 5)
+                    a := mul(a, b)
+                //stop()
+            }
+            let x := 5
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public {
+        assembly {
+
+            let a := 0
+            for {let i := 11
+                stop()
+            } lt(i, 10) {i := add(i, 1)
+        } {
+                a := shr(i, 2)
+                let b := shr(6, 5)
+                    a := mul(a, b)
+                //stop()
+            }
+            let x := 5
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public {
+        assembly {
+
+            let a := 0
+            for {let i := 11
+            } lt(i, 10) {i := add(i, 1)
+            //stop()
+        } {
+                a := shr(i, 2)
+                let b := shr(6, 5)
+                    a := mul(a, b)
+                stop()
+            }
+            let x := 5
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "unreachable assembly statement"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public {
+        assembly {
+
+            let a := 0
+            for {let i := 11
+            } lt(i, 10) {i := add(i, 1)
+            stop()
+        } {
+                a := shr(i, 2)
+                let b := shr(6, 5)
+                    a := mul(a, b)
+                //stop()
+            }
+            let x := 5
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}

+ 52 - 0
src/sema/assembly/tests/functions.rs

@@ -0,0 +1,52 @@
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
+
+#[test]
+fn repeated_names() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                function tryThis(a, a) {
+                    a := add(a, 4)
+                    if gt(a, 5) {
+                        leave
+                    }
+                    let b := add(a, 6)
+                    return(b, 0)
+                }
+            }
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "variable name 'a' already used in this scope"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                function tryThis(b, a) -> b {
+                    a := add(a, 4)
+                    if gt(a, 5) {
+                        leave
+                    }
+                    b := add(a, 6)
+                    return(b, 0)
+                }
+            }
+        }
+    }
+}    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "variable name 'b' already used in this scope"
+    ));
+}

+ 29 - 1
src/sema/assembly/tests/mod.rs

@@ -1,2 +1,30 @@
-//#![assembly(test)]
+#![cfg(test)]
+
+use crate::{ast, parse_and_resolve, FileResolver, Target};
+use solang_parser::Diagnostic;
+use std::ffi::OsStr;
+
+mod block;
 mod expression;
+mod for_loop;
+mod functions;
+mod statements;
+mod switch;
+mod types;
+
+pub(crate) fn parse(src: &'static str) -> ast::Namespace {
+    let mut cache = FileResolver::new();
+    cache.set_file_contents("test.sol", src.to_string());
+
+    parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Solana)
+}
+
+pub(crate) fn assert_message_in_diagnostics(diagnostics: &[Diagnostic], message: &str) -> bool {
+    for item in diagnostics {
+        if item.message == message {
+            return true;
+        }
+    }
+
+    false
+}

+ 375 - 0
src/sema/assembly/tests/statements.rs

@@ -0,0 +1,375 @@
+#![cfg(test)]
+
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
+
+#[test]
+fn variables_assignment_mismatch() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let x, y, z := 2
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "a single value cannot be assigned to multiple variables"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let x, y, z := add(4, 0x40af)
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "3 variables on the left hand side, but the function returns 1 values"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let x, y, z := foo(4, 0x40af)
+
+           function foo(a, b) -> ret1 : s32, ret2 : u64 {
+               ret1 := add(a, b)
+               ret2 := sub(b, a)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "3 variables on the left hand side, but the function returns 2 values"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let x, y := foo(4, 0x40af)
+
+           y := foo(3, 2)
+
+           function foo(a, b) -> ret1 : s32, ret2 : u64 {
+               ret1 := add(a, b)
+               ret2 := sub(b, a)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "1 variables on the left hand side, but the function returns 2 values"
+    ));
+}
+
+#[test]
+fn assignment() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let foo := 2
+
+           function foo(a, b) -> ret1 : s32, ret2 : u64 {
+               ret1 := add(a, b)
+               ret2 := sub(b, a)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "name 'foo' has been defined as a function"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let mulmod := 2
+
+           function foo(a, b) -> ret1 : s32, ret2 : u64 {
+               ret1 := add(a, b)
+               ret2 := sub(b, a)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "'mulmod' is a built-in function and cannot be a variable name"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           let verbatim_2 := 2
+
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "the prefix 'verbatim' is reserved for verbatim functions"
+    ));
+}
+
+#[test]
+fn top_level_function_calls() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           mod(3, 4)
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "top level function calls must not return anything"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           tryThis(2)
+
+           function tryThis(c) -> ret {
+               ret := div(c, 3)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "top level function calls must not return anything"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           tryThis(2)
+
+           function tryThis(c) {
+               let ret := div(c, 3)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}
+
+#[test]
+fn leave_statement() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           tryThis(2)
+
+            leave
+           function tryThis(c) {
+               let ret := div(c, 3)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "leave statement cannot be used outside a function"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           tryThis(2)
+
+           function tryThis(c) {
+               let ret := div(c, 3)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}
+
+#[test]
+fn continue_statement() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           tryThis(2)
+
+            continue
+           function tryThis(c) {
+               let ret := div(c, 3)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "continue statement outside a for loop"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let x := 0
+                for {
+                    let i := 0
+                } lt(i, 0x100) {
+                    i := add(i, 0x20)
+                } {
+                    x := add(x, mload(i))
+                    if lt(x, 30) {
+                        continue
+                    }
+                    x := add(x, 1)
+                }
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}
+
+#[test]
+fn break_statement() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+           tryThis(2)
+
+            break
+           function tryThis(c) {
+               let ret := div(c, 3)
+           }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "break statement outside a for loop"
+    ));
+
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            {
+                let x := 0
+                for {
+                    let i := 0
+                } lt(i, 0x100) {
+                    i := add(i, 0x20)
+                } {
+                    x := add(x, mload(i))
+                    if lt(x, 30) {
+                        break
+                    }
+                    x := add(x, 1)
+                }
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}

+ 113 - 0
src/sema/assembly/tests/switch.rs

@@ -0,0 +1,113 @@
+#![cfg(test)]
+
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
+
+#[test]
+fn case_not_literal() {
+    let file = r#"
+    contract testTypes {
+    function testAsm() public view {
+        assembly {
+            let x := add(7, 6)
+            let y : u32 := 0x90
+            switch x
+            case 0 { revert(0, 0)}
+            case y { mstore(0, 0x40)}
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"unrecognised token `y', expected "false", "true", hexnumber, hexstring, number, string"#
+    ));
+}
+
+#[test]
+fn case_after_default() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public view {
+        assembly {
+            let x := add(7, 6)
+            let y : u32 := 0x90
+            switch x
+            case 0 { revert(0, 0)}
+            default { mstore(0, 0x40)}
+            case 2 { x := shr(y, 2)}
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"unrecognised token `case', expected "address", "bool", "break", "continue", "for", "function", "if", "leave", "let", "return", "revert", "switch", "{", "}", identifier"#
+    ));
+}
+
+#[test]
+fn double_default() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public view {
+        assembly {
+            let x := add(7, 6)
+            let y : u32 := 0x90
+            switch x
+            case 0 { revert(0, 0)}
+            default { mstore(0, 0x40)}
+            default { x := shr(y, 2)}
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        r#"unrecognised token `default', expected "address", "bool", "break", "continue", "for", "function", "if", "leave", "let", "return", "revert", "switch", "{", "}", identifier"#
+    ));
+}
+
+#[test]
+fn correct_switch() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public pure {
+        assembly {
+            function power(base, exponent) -> result {
+                switch exponent
+                case 0 {
+                    result := 1
+                }
+                case 1 {
+                    result := base
+                }
+                default {
+                    result := power(mul(base, base), div(exponent, 2))
+                    switch mod(exponent, 2)
+                    case 1 {
+                        result := mul(base, result)
+                    }
+                }
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert_eq!(ns.diagnostics.len(), 2);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "found contract ‘testTypes’"
+    ));
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "evm assembly not supported on target solana"
+    ));
+}

+ 75 - 0
src/sema/assembly/tests/types.rs

@@ -0,0 +1,75 @@
+#![cfg(test)]
+
+use crate::sema::assembly::tests::{assert_message_in_diagnostics, parse};
+
+#[test]
+fn type_not_found() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public view {
+        assembly {
+            let x : s120 := 230
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "the specified type 's120' does not exist"
+    ));
+}
+
+#[test]
+fn incompatible_argument() {
+    let file = r#"
+contract testTypes {
+    function testAsm() public view {
+        assembly {
+            let x := add(log0(0, 1), 5)
+        }
+    }
+}
+    "#;
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "builtin function 'log0' returns nothing"
+    ));
+
+    let file = r#"
+    contract testTypes {
+    function testAsm() public view {
+        assembly {
+            let x := add(doThis(1), doThat(2))
+            let y := add(x, foo(3))
+
+            function doThis(a) {
+                log0(a, 1)
+            }
+
+            function doThat(a) -> ret {
+                ret := mul(a, 2)
+            }
+
+            function foo(a) -> ret1, ret2 {
+                ret1 := mul(a, 2)
+                ret2 := mul(a, 3)
+            }
+        }
+    }
+}
+    "#;
+
+    let ns = parse(file);
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "function 'doThis' returns nothing"
+    ));
+
+    assert!(assert_message_in_diagnostics(
+        &ns.diagnostics,
+        "function 'foo' has multiple returns and cannot be used in this scope"
+    ));
+}

+ 65 - 0
src/sema/assembly/types.rs

@@ -1,7 +1,11 @@
 use crate::ast::{Namespace, Type};
+use crate::pt::CodeLocation;
+use crate::sema::assembly::expression::AssemblyExpression;
+use crate::sema::assembly::functions::FunctionsTable;
 use solang_parser::pt::Identifier;
 use solang_parser::Diagnostic;
 
+/// Retrieves an YUL type from a keyword
 pub(crate) fn get_type_from_string(text: &str) -> Option<Type> {
     match text {
         "bool" => Some(Type::Bool),
@@ -19,6 +23,7 @@ pub(crate) fn get_type_from_string(text: &str) -> Option<Type> {
     }
 }
 
+/// Given a parse tree identifier, retrieve its type or return the default for YUL
 pub(crate) fn get_default_type_from_identifier(
     ty: &Option<Identifier>,
     ns: &mut Namespace,
@@ -37,3 +42,63 @@ pub(crate) fn get_default_type_from_identifier(
         Ok(Type::Uint(256))
     }
 }
+
+/// Performs checks on whether it is possible to retrieve a type from an expression
+pub(crate) fn verify_type_from_expression(
+    expr: &AssemblyExpression,
+    function_table: &FunctionsTable,
+) -> Result<Type, Diagnostic> {
+    match expr {
+        AssemblyExpression::BoolLiteral(..) => Ok(Type::Bool),
+
+        AssemblyExpression::NumberLiteral(_, _, ty)
+        | AssemblyExpression::StringLiteral(_, _, ty)
+        | AssemblyExpression::AssemblyLocalVariable(_, ty, _)
+        | AssemblyExpression::ConstantVariable(_, ty, ..)
+        | AssemblyExpression::SolidityLocalVariable(_, ty, None, _) => Ok(ty.clone()),
+
+        AssemblyExpression::SolidityLocalVariable(_, _, Some(_), _)
+        | AssemblyExpression::MemberAccess(..)
+        | AssemblyExpression::StorageVariable(..) => Ok(Type::Uint(256)),
+
+        AssemblyExpression::BuiltInCall(_, ty, _) => {
+            let prototype = ty.get_prototype_info();
+            if prototype.no_returns == 0 {
+                Err(Diagnostic::error(
+                    expr.loc(),
+                    format!("builtin function '{}' returns nothing", prototype.name),
+                ))
+            } else if prototype.no_returns > 1 {
+                Err(Diagnostic::error(
+                    expr.loc(),
+                    format!(
+                        "builtin function '{}' has multiple returns and cannot be used in this scope",
+                        prototype.name
+                    ),
+                ))
+            } else {
+                Ok(Type::Uint(256))
+            }
+        }
+
+        AssemblyExpression::FunctionCall(_, function_no, ..) => {
+            let func = function_table.get(*function_no).unwrap();
+            if func.returns.is_empty() {
+                Err(Diagnostic::error(
+                    expr.loc(),
+                    format!("function '{}' returns nothing", func.id.name),
+                ))
+            } else if func.returns.len() > 1 {
+                Err(Diagnostic::error(
+                    expr.loc(),
+                    format!(
+                        "function '{}' has multiple returns and cannot be used in this scope",
+                        func.id.name
+                    ),
+                ))
+            } else {
+                Ok(func.returns[0].ty.clone())
+            }
+        }
+    }
+}

+ 5 - 4
src/sema/ast.rs

@@ -3,10 +3,11 @@ use crate::codegen::cfg::ControlFlowGraph;
 pub use crate::parser::diagnostics::*;
 use crate::parser::pt;
 use crate::parser::pt::{CodeLocation, OptionalCodeLocation};
-use crate::sema::assembly::AssemblyStatement;
+use crate::sema::assembly::InlineAssembly;
 use crate::Target;
 use num_bigint::BigInt;
 use num_rational::BigRational;
+use std::sync::Arc;
 use std::{
     collections::{BTreeMap, HashMap},
     fmt,
@@ -1395,7 +1396,7 @@ pub enum Statement {
         unchecked: bool,
         statements: Vec<Statement>,
     },
-    VariableDecl(pt::Loc, usize, Parameter, Option<Expression>),
+    VariableDecl(pt::Loc, usize, Parameter, Option<Arc<Expression>>),
     If(pt::Loc, bool, Expression, Vec<Statement>, Vec<Statement>),
     While(pt::Loc, bool, Expression, Vec<Statement>),
     For {
@@ -1421,7 +1422,7 @@ pub enum Statement {
     },
     TryCatch(pt::Loc, bool, TryCatch),
     Underscore(pt::Loc),
-    AssemblyBlock(Vec<AssemblyStatement>),
+    Assembly(InlineAssembly),
 }
 
 #[derive(Clone, Debug)]
@@ -1536,7 +1537,7 @@ impl Statement {
             Statement::Delete(..) => true,
             Statement::Continue(_) | Statement::Break(_) | Statement::Return(..) => false,
             Statement::For { reachable, .. } | Statement::TryCatch(_, reachable, _) => *reachable,
-            Statement::AssemblyBlock(_) => unimplemented!("Assembly block ast not ready"),
+            Statement::Assembly(_) => unimplemented!("Assembly block ast not ready"),
         }
     }
 }

+ 1 - 0
src/sema/contracts.rs

@@ -208,6 +208,7 @@ fn resolve_base_args(
             unchecked: false,
             constant: false,
             lvalue: false,
+            yul_function: false,
         };
 
         for base in &def.base {

+ 1 - 1
src/sema/dotgraphviz.rs

@@ -1562,7 +1562,7 @@ impl Dot {
                     );
                 }
 
-                Statement::AssemblyBlock(_) => {
+                Statement::Assembly(_) => {
                     unimplemented!("Assembly block graphviz not ready yet");
                 }
             }

+ 2 - 0
src/sema/expression.rs

@@ -1423,6 +1423,8 @@ pub struct ExprContext {
     pub constant: bool,
     /// Are we resolving an l-value
     pub lvalue: bool,
+    /// Are we resolving a yul function (it cannot have external dependencies)
+    pub yul_function: bool,
 }
 
 /// Resolve a parsed expression into an AST expression. The resolve_to argument is a hint to what

+ 1 - 0
src/sema/mod.rs

@@ -1502,6 +1502,7 @@ impl ast::Namespace {
             function_no,
             constant: true,
             lvalue: false,
+            yul_function: false,
         };
 
         let size_expr = expression(

+ 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::AssemblyBlock(_) => {
+            Statement::Assembly(_) => {
                 unimplemented!("Assembly block mutability not ready yet");
             }
         }

+ 1 - 1
src/sema/printer.rs

@@ -670,7 +670,7 @@ fn print_statement(stmts: &[Statement], func: &Function, ns: &Namespace) -> Vec<
                 Tree::Branch(String::from("try-catch"), list)
             }
             Statement::Underscore(_) => Tree::Leaf(String::from("underscore")),
-            Statement::AssemblyBlock(_) => unimplemented!("Assembly block printer not ready yet"),
+            Statement::Assembly(_) => unimplemented!("Assembly block printer not ready yet"),
         });
     }
 

+ 41 - 16
src/sema/statements.rs

@@ -10,12 +10,12 @@ use crate::parser::pt;
 use crate::parser::pt::CatchClause;
 use crate::parser::pt::CodeLocation;
 use crate::parser::pt::OptionalCodeLocation;
-use crate::sema::{
-    builtin,
-    symtable::VariableUsage,
-    unused_variable::{assigned_variable, check_function_call, used_variable},
-};
+use crate::sema::assembly::resolve_inline_assembly;
+use crate::sema::builtin;
+use crate::sema::symtable::{VariableInitializer, VariableUsage};
+use crate::sema::unused_variable::{assigned_variable, check_function_call, used_variable};
 use std::collections::{BTreeMap, HashMap, HashSet};
+use std::sync::Arc;
 
 pub fn resolve_function_body(
     def: &pt::FunctionDefinition,
@@ -34,6 +34,7 @@ pub fn resolve_function_body(
         unchecked: false,
         constant: false,
         lvalue: false,
+        yul_function: false,
     };
 
     // first add function parameters
@@ -44,7 +45,7 @@ pub fn resolve_function_body(
                 name,
                 ns.functions[function_no].params[i].ty.clone(),
                 ns,
-                None,
+                VariableInitializer::Solidity(None),
                 VariableUsage::Parameter,
                 p.storage.clone(),
             ) {
@@ -209,7 +210,7 @@ pub fn resolve_function_body(
                 name,
                 ret.ty.clone(),
                 ns,
-                None,
+                VariableInitializer::Solidity(None),
                 VariableUsage::ReturnVariable,
                 None,
             ) {
@@ -228,7 +229,7 @@ pub fn resolve_function_body(
                     &id,
                     ret.ty.clone(),
                     ns,
-                    None,
+                    VariableInitializer::Solidity(None),
                     VariableUsage::AnonymousReturnVariable,
                     None,
                 )
@@ -327,7 +328,14 @@ fn statement(
 
                 used_variable(ns, &expr, symtable);
 
-                Some(cast(&expr.loc(), expr, &var_ty, true, ns, diagnostics)?)
+                Some(Arc::new(cast(
+                    &expr.loc(),
+                    expr,
+                    &var_ty,
+                    true,
+                    ns,
+                    diagnostics,
+                )?))
             } else {
                 None
             };
@@ -336,7 +344,7 @@ fn statement(
                 &decl.name,
                 var_ty.clone(),
                 ns,
-                initializer.clone(),
+                VariableInitializer::Solidity(initializer.clone()),
                 VariableUsage::LocalVariable,
                 decl.storage.clone(),
             ) {
@@ -791,12 +799,29 @@ fn statement(
 
             Ok(true)
         }
-        pt::Statement::Assembly { loc, .. } => {
+        pt::Statement::Assembly {
+            loc,
+            dialect,
+            block,
+        } => {
+            // TODO: Remove this error when sema is ready
             ns.diagnostics.push(Diagnostic::error(
                 *loc,
                 format!("evm assembly not supported on target {}", ns.target),
             ));
-            Err(())
+
+            if dialect.is_some() && dialect.as_ref().unwrap().string != "evmasm" {
+                ns.diagnostics.push(Diagnostic::error(
+                    dialect.as_ref().unwrap().loc,
+                    "only evmasm dialect is supported".to_string(),
+                ));
+                return Err(());
+            }
+
+            let resolved_asm =
+                resolve_inline_assembly(loc, &block.statements, context, symtable, ns);
+            res.push(Statement::Assembly(resolved_asm.0));
+            Ok(resolved_asm.1)
         }
         pt::Statement::Revert(loc, error, args) => {
             if let Some(error) = error {
@@ -1216,7 +1241,7 @@ fn destructure(
                     name,
                     ty.clone(),
                     ns,
-                    None,
+                    VariableInitializer::Solidity(None),
                     VariableUsage::DestructureVariable,
                     storage.clone(),
                 ) {
@@ -1908,7 +1933,7 @@ fn try_catch(
                         name,
                         ret_ty.clone(),
                         ns,
-                        None,
+                        VariableInitializer::Solidity(None),
                         VariableUsage::TryCatchReturns,
                         storage.clone(),
                     ) {
@@ -2023,7 +2048,7 @@ fn try_catch(
                             name,
                             catch_ty,
                             ns,
-                            None,
+                            VariableInitializer::Solidity(None),
                             VariableUsage::TryCatchErrorBytes,
                             param.storage.clone(),
                         ) {
@@ -2104,7 +2129,7 @@ fn try_catch(
                         name,
                         Type::String,
                         ns,
-                        None,
+                        VariableInitializer::Solidity(None),
                         VariableUsage::TryCatchErrorString,
                         param.storage.clone(),
                     ) {

+ 47 - 4
src/sema/symtable.rs

@@ -1,6 +1,8 @@
 use indexmap::IndexMap;
+use solang_parser::diagnostics::{ErrorType, Level, Note};
 use std::collections::{HashMap, HashSet, LinkedList};
 use std::str;
+use std::sync::Arc;
 
 use super::ast::{Diagnostic, Namespace, Type};
 use crate::parser::pt;
@@ -15,10 +17,25 @@ pub struct Variable {
     pub assigned: bool,
     pub read: bool,
     pub usage_type: VariableUsage,
-    pub initializer: Option<Expression>,
+    pub initializer: VariableInitializer,
     pub storage_location: Option<pt::StorageLocation>,
 }
 
+#[derive(Clone, Debug)]
+pub enum VariableInitializer {
+    Solidity(Option<Arc<Expression>>),
+    Assembly(bool),
+}
+
+impl VariableInitializer {
+    pub fn has_initializer(&self) -> bool {
+        match self {
+            VariableInitializer::Solidity(expr) => expr.is_some(),
+            VariableInitializer::Assembly(initialized) => *initialized,
+        }
+    }
+}
+
 impl Variable {
     pub fn is_reference(&self) -> bool {
         // If the variable has the memory or storage keyword, it can be a reference to another variable.
@@ -27,11 +44,11 @@ impl Variable {
             self.storage_location,
             Some(pt::StorageLocation::Memory(_)) | Some(pt::StorageLocation::Storage(_))
         ) {
-            if let Some(expr) = &self.initializer {
+            if let VariableInitializer::Solidity(Some(expr)) = &self.initializer {
                 // If the initializer is an array allocation, a constructor or a struct literal,
                 // the variable is not a reference to another.
                 return !matches!(
-                    expr,
+                    **expr,
                     Expression::AllocDynamicArray(..)
                         | Expression::ArrayLiteral(..)
                         | Expression::Constructor { .. }
@@ -85,7 +102,7 @@ impl Symtable {
         id: &pt::Identifier,
         ty: Type,
         ns: &mut Namespace,
-        initializer: Option<Expression>,
+        initializer: VariableInitializer,
         usage_type: VariableUsage,
         storage_location: Option<pt::StorageLocation>,
     ) -> Option<usize> {
@@ -129,6 +146,32 @@ impl Symtable {
         Some(pos)
     }
 
+    pub fn exclusive_add(
+        &mut self,
+        id: &pt::Identifier,
+        ty: Type,
+        ns: &mut Namespace,
+        initializer: VariableInitializer,
+        usage_type: VariableUsage,
+        storage_location: Option<pt::StorageLocation>,
+    ) -> Option<usize> {
+        if let Some(var) = self.find(&id.name) {
+            ns.diagnostics.push(Diagnostic {
+                level: Level::Error,
+                ty: ErrorType::DeclarationError,
+                pos: id.loc,
+                message: format!("variable name '{}' already used in this scope", id.name),
+                notes: vec![Note {
+                    pos: var.id.loc,
+                    message: "found previous declaration here".to_string(),
+                }],
+            });
+            return None;
+        }
+
+        self.add(id, ty, ns, initializer, usage_type, storage_location)
+    }
+
     pub fn find(&self, name: &str) -> Option<&Variable> {
         for scope in &self.names {
             if let Some(n) = scope.0.get(name) {

+ 3 - 2
src/sema/unused_variable.rs

@@ -275,7 +275,7 @@ pub fn emit_warning_local_variable(variable: &symtable::Variable) -> Option<Diag
         }
 
         VariableUsage::LocalVariable => {
-            let assigned = variable.initializer.is_some() || variable.assigned;
+            let assigned = variable.initializer.has_initializer() || variable.assigned;
             if !assigned && !variable.read {
                 return Some(generate_unused_warning(
                     variable.id.loc,
@@ -360,7 +360,8 @@ pub fn emit_warning_local_variable(variable: &symtable::Variable) -> Option<Diag
             None
         }
         VariableUsage::AssemblyLocalVariable => {
-            unimplemented!("Variable usage not implemented for YUL")
+            //unimplemented!("Variable usage not implemented for YUL")
+            None
         }
         VariableUsage::AnonymousReturnVariable => None,
     }

+ 1 - 0
src/sema/variables.rs

@@ -246,6 +246,7 @@ pub fn var_decl(
             function_no: None,
             constant,
             lvalue: false,
+            yul_function: false,
         };
 
         match expression(

+ 0 - 12
tests/contract_testcases/solana/assembly/parse.dot

@@ -1,12 +0,0 @@
-strict digraph "tests/contract_testcases/solana/assembly/parse.sol" {
-	contract [label="contract foo\ntests/contract_testcases/solana/assembly/parse.sol:2:9-22"]
-	get [label="function get\ncontract: foo\ntests/contract_testcases/solana/assembly/parse.sol:3:13-51\nsignature get()\nvisibility public\nmutability nonpayable"]
-	returns [label="returns\nbytes4 "]
-	diagnostic [label="found contract ‘foo’\nlevel Debug\ntests/contract_testcases/solana/assembly/parse.sol:2:9-22"]
-	diagnostic_7 [label="evm assembly not supported on target solana\nlevel Error\ntests/contract_testcases/solana/assembly/parse.sol:4:17-7:18"]
-	contracts -> contract
-	contract -> get [label="function"]
-	get -> returns [label="returns"]
-	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_7 [label="Error"]
-}

+ 0 - 9
tests/contract_testcases/solana/assembly/parse.sol

@@ -1,9 +0,0 @@
-
-        contract foo {
-            function get() public returns (bytes4) {
-                assembly {
-                    let returndata_size := mload(returndata)
-                    revert(add(32, returndata), returndata_size)
-                }
-            }
-        }

+ 0 - 10
tests/contract_testcases/solana/return_in_asm.dot

@@ -1,10 +0,0 @@
-strict digraph "tests/contract_testcases/solana/return_in_asm.sol" {
-	contract [label="contract Contract\ntests/contract_testcases/solana/return_in_asm.sol:1:1-19"]
-	node_3 [label="constructor \ncontract: Contract\ntests/contract_testcases/solana/return_in_asm.sol:2:5-19\nsignature ()\nvisibility public\nmutability nonpayable"]
-	diagnostic [label="found contract ‘Contract’\nlevel Debug\ntests/contract_testcases/solana/return_in_asm.sol:1:1-19"]
-	diagnostic_6 [label="evm assembly not supported on target solana\nlevel Error\ntests/contract_testcases/solana/return_in_asm.sol:3:9-5:10"]
-	contracts -> contract
-	contract -> node_3 [label="constructor"]
-	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_6 [label="Error"]
-}

+ 0 - 7
tests/contract_testcases/solana/return_in_asm.sol

@@ -1,7 +0,0 @@
-contract Contract {
-    constructor() {
-        assembly {
-            return(0, 0)
-        }
-    }
-}