Переглянути джерело

Split resolver into sema and codegen

The new sema stage ensures the Solidity is valid, and has all variables
resolved. The output is an AST. The AST can be used for a Language
Server.

A seperate new stage codegen converts the AST to a CFG.

Previously, the parse tree was converted into a CFG directly. This makes
the code much cleaner, since less work has to be done in each stage.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 роки тому
батько
коміт
3e1748edba
52 змінених файлів з 11765 додано та 11581 видалено
  1. 1 1
      clippy.toml
  2. 2 1
      docs/installing.rst
  3. 1 1
      src/abi/ethereum.rs
  4. 1 1
      src/abi/mod.rs
  5. 28 32
      src/abi/substrate.rs
  6. 13 10
      src/bin/solang.rs
  7. 956 0
      src/codegen/cfg.rs
  8. 1127 0
      src/codegen/expression.rs
  9. 50 0
      src/codegen/mod.rs
  10. 786 0
      src/codegen/statements.rs
  11. 313 0
      src/codegen/storage.rs
  12. 82 94
      src/emit/ethabiencoder.rs
  13. 11 12
      src/emit/ewasm.rs
  14. 118 119
      src/emit/mod.rs
  15. 6 6
      src/emit/sabre.rs
  16. 70 75
      src/emit/substrate.rs
  17. 23 18
      src/lib.rs
  18. 4 0
      src/parser/mod.rs
  19. 2 0
      src/parser/pt.rs
  20. 6 5
      src/parser/solidity.lalrpop
  21. 117 117
      src/parser/solidity.rs
  22. 0 482
      src/resolver/builtin.rs
  23. 0 2352
      src/resolver/cfg.rs
  24. 0 133
      src/resolver/eval.rs
  25. 0 5604
      src/resolver/expression.rs
  26. 0 1442
      src/resolver/mod.rs
  27. 0 493
      src/resolver/storage.rs
  28. 0 0
      src/sema/address.rs
  29. 818 0
      src/sema/ast.rs
  30. 183 0
      src/sema/builtin.rs
  31. 128 0
      src/sema/eval.rs
  32. 4268 0
      src/sema/expression.rs
  33. 52 53
      src/sema/functions.rs
  34. 772 0
      src/sema/mod.rs
  35. 1190 0
      src/sema/statements.rs
  36. 0 0
      src/sema/structs.rs
  37. 135 0
      src/sema/symtable.rs
  38. 27 52
      src/sema/types.rs
  39. 19 79
      src/sema/variables.rs
  40. 2 2
      tests/loops.rs
  41. 69 63
      tests/substrate_arrays/mod.rs
  42. 49 46
      tests/substrate_calls/mod.rs
  43. 43 40
      tests/substrate_contracts/mod.rs
  44. 10 7
      tests/substrate_enums/mod.rs
  45. 25 25
      tests/substrate_expressions/mod.rs
  46. 51 48
      tests/substrate_functions/mod.rs
  47. 29 20
      tests/substrate_mappings/mod.rs
  48. 50 50
      tests/substrate_primitives/mod.rs
  49. 22 22
      tests/substrate_strings/mod.rs
  50. 41 26
      tests/substrate_structs/mod.rs
  51. 53 38
      tests/substrate_value/mod.rs
  52. 12 12
      tests/variables.rs

+ 1 - 1
clippy.toml

@@ -1,2 +1,2 @@
 too-many-arguments-threshold = 11
-cognitive-complexity-threshold = 37
+cognitive-complexity-threshold = 37

+ 2 - 1
docs/installing.rst

@@ -183,4 +183,5 @@ And on Windows, assuming *installdir* was ``C:\Users\User\solang-llvm``:
 
 .. code-block::
 
-	set PATH=%PATH%;C:\Users\User\solang-llvm\bin
+	set PATH=%PATH%;C:\Users\User\solang-llvm\bin
+

+ 1 - 1
src/abi/ethereum.rs

@@ -1,6 +1,6 @@
 // ethereum style ABIs
 use parser::pt;
-use resolver::{Namespace, Type};
+use sema::ast::{Namespace, Type};
 use serde::Serialize;
 
 #[derive(Serialize)]

+ 1 - 1
src/abi/mod.rs

@@ -1,4 +1,4 @@
-use resolver::Namespace;
+use sema::ast::Namespace;
 use Target;
 
 pub mod ethereum;

+ 28 - 32
src/abi/substrate.rs

@@ -1,7 +1,7 @@
 // Parity Substrate style ABIs/metadata
 use num_traits::ToPrimitive;
 use parser::pt;
-use resolver;
+use sema::ast;
 use serde::{Deserialize, Serialize};
 
 /// Substrate contracts abi consists of a a registry of strings and types, the contract itself
@@ -294,7 +294,7 @@ impl Registry {
 
     /// Returns index to builtin type in registry. Type is added if not already present
     #[allow(dead_code)]
-    fn builtin_enum_type(&mut self, e: &resolver::EnumDecl) -> usize {
+    fn builtin_enum_type(&mut self, e: &ast::EnumDecl) -> usize {
         let length = self.types.len();
         let name = self.string(&e.name);
 
@@ -354,7 +354,7 @@ pub fn load(bs: &str) -> Result<Metadata, serde_json::error::Error> {
     serde_json::from_str(bs)
 }
 
-pub fn gen_abi(contract_no: usize, ns: &resolver::Namespace) -> Metadata {
+pub fn gen_abi(contract_no: usize, ns: &ast::Namespace) -> Metadata {
     let mut registry = Registry::new();
 
     let fields = ns.contracts[contract_no]
@@ -373,7 +373,7 @@ pub fn gen_abi(contract_no: usize, ns: &resolver::Namespace) -> Metadata {
         .variables
         .iter()
         .filter_map(|v| {
-            if let resolver::ContractVariableType::Storage(storage) = &v.var {
+            if let ast::ContractVariableType::Storage(storage) = &v.var {
                 if !v.ty.is_mapping() {
                     Some(StorageLayout {
                         name: registry.string(&v.name),
@@ -473,28 +473,27 @@ pub fn gen_abi(contract_no: usize, ns: &resolver::Namespace) -> Metadata {
     }
 }
 
-fn ty_to_abi(ty: &resolver::Type, ns: &resolver::Namespace, registry: &mut Registry) -> ParamType {
+fn ty_to_abi(ty: &ast::Type, ns: &ast::Namespace, registry: &mut Registry) -> ParamType {
     match ty {
         /* clike_enums are broken in polkadot. Use u8 for now.
-        resolver::Type::Enum(n) => ParamType {
+        ast::Type::Enum(n) => ParamType {
             ty: registry.builtin_enum_type(&contract.enums[*n]),
             display_name: vec![registry.string(&contract.enums[*n].name)],
         },
         */
-        resolver::Type::Enum(_) => ParamType {
+        ast::Type::Enum(_) => ParamType {
             ty: registry.builtin_type("u8"),
             display_name: vec![registry.string("u8")],
         },
-        resolver::Type::Bytes(n) => {
+        ast::Type::Bytes(n) => {
             let elem = registry.builtin_type("u8");
             ParamType {
                 ty: registry.builtin_array_type(elem, *n as usize),
                 display_name: vec![],
             }
         }
-        resolver::Type::Undef => unreachable!(),
-        resolver::Type::Mapping(_, _) => unreachable!(),
-        resolver::Type::Array(ty, dims) => {
+        ast::Type::Mapping(_, _) => unreachable!(),
+        ast::Type::Array(ty, dims) => {
             let mut param_ty = ty_to_abi(ty, ns, registry);
 
             for d in dims {
@@ -510,13 +509,13 @@ fn ty_to_abi(ty: &resolver::Type, ns: &resolver::Namespace, registry: &mut Regis
 
             param_ty
         }
-        resolver::Type::StorageRef(ty) => ty_to_abi(ty, ns, registry),
-        resolver::Type::Ref(ty) => ty_to_abi(ty, ns, registry),
-        resolver::Type::Bool
-        | resolver::Type::Uint(_)
-        | resolver::Type::Int(_)
-        | resolver::Type::Address(_)
-        | resolver::Type::Contract(_) => {
+        ast::Type::StorageRef(ty) => ty_to_abi(ty, ns, registry),
+        ast::Type::Ref(ty) => ty_to_abi(ty, ns, registry),
+        ast::Type::Bool
+        | ast::Type::Uint(_)
+        | ast::Type::Int(_)
+        | ast::Type::Address(_)
+        | ast::Type::Contract(_) => {
             let scalety = primitive_to_string(ty.clone());
 
             ParamType {
@@ -524,7 +523,7 @@ fn ty_to_abi(ty: &resolver::Type, ns: &resolver::Namespace, registry: &mut Regis
                 display_name: vec![registry.string(&scalety)],
             }
         }
-        resolver::Type::Struct(n) => {
+        ast::Type::Struct(n) => {
             let def = &ns.structs[*n];
             let fields = def
                 .fields
@@ -539,7 +538,7 @@ fn ty_to_abi(ty: &resolver::Type, ns: &resolver::Namespace, registry: &mut Regis
                 display_name: vec![],
             }
         }
-        resolver::Type::DynamicBytes => {
+        ast::Type::DynamicBytes => {
             let elem = registry.builtin_type("u8");
 
             ParamType {
@@ -547,30 +546,27 @@ fn ty_to_abi(ty: &resolver::Type, ns: &resolver::Namespace, registry: &mut Regis
                 display_name: vec![registry.string("Vec")],
             }
         }
-        resolver::Type::String => ParamType {
+        ast::Type::String => ParamType {
             ty: registry.string_type(),
             display_name: vec![registry.string("str")],
         },
+        _ => unreachable!(),
     }
 }
 
 // For a given primitive, give the name as Substrate would like it (i.e. 64 bits
 // signed int is i64, not int64).
-fn primitive_to_string(ty: resolver::Type) -> String {
+fn primitive_to_string(ty: ast::Type) -> String {
     match ty {
-        resolver::Type::Bool => "bool".into(),
-        resolver::Type::Uint(n) => format!("u{}", n),
-        resolver::Type::Int(n) => format!("i{}", n),
-        resolver::Type::Contract(_) | resolver::Type::Address(_) => "address".into(),
+        ast::Type::Bool => "bool".into(),
+        ast::Type::Uint(n) => format!("u{}", n),
+        ast::Type::Int(n) => format!("i{}", n),
+        ast::Type::Contract(_) | ast::Type::Address(_) => "address".into(),
         _ => unreachable!(),
     }
 }
 
-fn parameter_to_abi(
-    param: &resolver::Parameter,
-    ns: &resolver::Namespace,
-    registry: &mut Registry,
-) -> Param {
+fn parameter_to_abi(param: &ast::Parameter, ns: &ast::Namespace, registry: &mut Registry) -> Param {
     Param {
         name: registry.string(&param.name),
         ty: ty_to_abi(&param.ty, ns, registry),
@@ -578,7 +574,7 @@ fn parameter_to_abi(
 }
 
 /// Given an u32 selector, generate a byte string like: "[\"0xF8\",\"0x1E\",\"0x7E\",\"0x1A\"]"
-fn render_selector(f: &resolver::FunctionDecl) -> String {
+fn render_selector(f: &ast::Function) -> String {
     format!(
         "[{}]",
         f.selector()

+ 13 - 10
src/bin/solang.rs

@@ -10,6 +10,7 @@ use std::io::prelude::*;
 use std::path::{Path, PathBuf};
 
 use solang::abi;
+use solang::codegen::codegen;
 use solang::output;
 
 #[derive(Serialize)]
@@ -146,27 +147,29 @@ fn process_filename(
         .expect("something went wrong reading the file");
 
     // resolve phase
-    let (ns, errors) = solang::parse_and_resolve(&contents, target);
+    let mut ns = solang::parse_and_resolve(&contents, target);
 
     if matches.is_present("STD-JSON") {
-        let mut out = output::message_as_json(filename, &contents, &errors);
+        let mut out = output::message_as_json(filename, &contents, &ns.diagnostics);
         json.errors.append(&mut out);
     } else {
-        output::print_messages(filename, &contents, &errors, verbose);
+        output::print_messages(filename, &contents, &ns.diagnostics, verbose);
     }
 
-    let ns = match ns {
-        Some(ns) => ns,
-        None => std::process::exit(1),
-    };
-
     if ns.contracts.is_empty() {
         eprintln!("{}: error: no contracts found", filename);
         std::process::exit(1);
     }
 
+    // codegen all the contracts
+    for contract_no in 0..ns.contracts.len() {
+        codegen(contract_no, &mut ns);
+    }
+
     // emit phase
-    for (contract_no, resolved_contract) in ns.contracts.iter().enumerate() {
+    for contract_no in 0..ns.contracts.len() {
+        let resolved_contract = &ns.contracts[contract_no];
+
         if let Some("cfg") = matches.value_of("EMIT") {
             println!("{}", resolved_contract.print_to_string(&ns));
             continue;
@@ -323,7 +326,7 @@ fn process_filename(
             let mut file = File::create(wasm_filename).unwrap();
             file.write_all(&wasm).unwrap();
 
-            let (abi_bytes, abi_ext) = ns.abi(contract_no, verbose);
+            let (abi_bytes, abi_ext) = abi::generate_abi(contract_no, &ns, verbose);
             let abi_filename = output_file(&contract.name, abi_ext);
 
             if verbose {

+ 956 - 0
src/codegen/cfg.rs

@@ -0,0 +1,956 @@
+use num_bigint::BigInt;
+use std::collections::HashSet;
+use std::fmt;
+use std::str;
+
+use super::statements::{statement, LoopScopes};
+use hex;
+use parser::pt;
+use sema::ast::{Contract, Expression, Namespace, Parameter, StringLocation, Type};
+use sema::symtable::Symtable;
+
+#[allow(clippy::large_enum_variant)]
+pub enum Instr {
+    ClearStorage {
+        ty: Type,
+        storage: Expression,
+    },
+    SetStorage {
+        ty: Type,
+        local: usize,
+        storage: Expression,
+    },
+    SetStorageBytes {
+        local: usize,
+        storage: Box<Expression>,
+        offset: Box<Expression>,
+    },
+    Set {
+        res: usize,
+        expr: Expression,
+    },
+    Eval {
+        expr: Expression,
+    },
+    Constant {
+        res: usize,
+        constant: usize,
+    },
+    Call {
+        res: Vec<usize>,
+        func: usize,
+        args: Vec<Expression>,
+    },
+    Return {
+        value: Vec<Expression>,
+    },
+    Branch {
+        bb: usize,
+    },
+    BranchCond {
+        cond: Expression,
+        true_: usize,
+        false_: usize,
+    },
+    Store {
+        dest: Expression,
+        pos: usize,
+    },
+    AssertFailure {
+        expr: Option<Expression>,
+    },
+    Print {
+        expr: Expression,
+    },
+    Constructor {
+        success: Option<usize>,
+        res: usize,
+        contract_no: usize,
+        constructor_no: usize,
+        args: Vec<Expression>,
+        value: Option<Expression>,
+        gas: Expression,
+        salt: Option<Expression>,
+    },
+    ExternalCall {
+        success: Option<usize>,
+        address: Expression,
+        contract_no: Option<usize>,
+        function_no: usize,
+        args: Vec<Expression>,
+        value: Expression,
+        gas: Expression,
+    },
+    AbiDecode {
+        res: Vec<usize>,
+        selector: Option<u32>,
+        exception: Option<usize>,
+        tys: Vec<Parameter>,
+        data: Expression,
+    },
+    Unreachable,
+    SelfDestruct {
+        recipient: Expression,
+    },
+    Hash {
+        res: usize,
+        hash: HashTy,
+        expr: Expression,
+    },
+}
+
+#[derive(Clone, PartialEq)]
+pub enum HashTy {
+    Keccak256,
+    Ripemd160,
+    Sha256,
+    Blake2_256,
+    Blake2_128,
+}
+
+impl fmt::Display for HashTy {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            HashTy::Keccak256 => write!(f, "keccak256"),
+            HashTy::Ripemd160 => write!(f, "ripemd160"),
+            HashTy::Sha256 => write!(f, "sha256"),
+            HashTy::Blake2_128 => write!(f, "blake2_128"),
+            HashTy::Blake2_256 => write!(f, "blake2_256"),
+        }
+    }
+}
+
+pub struct BasicBlock {
+    pub phis: Option<HashSet<usize>>,
+    pub name: String,
+    pub instr: Vec<Instr>,
+}
+
+impl BasicBlock {
+    fn add(&mut self, ins: Instr) {
+        self.instr.push(ins);
+    }
+}
+
+#[derive(Default)]
+pub struct ControlFlowGraph {
+    pub vars: Vec<Variable>,
+    pub bb: Vec<BasicBlock>,
+    current: usize,
+    pub writes_contract_storage: bool,
+}
+
+impl ControlFlowGraph {
+    pub fn new() -> Self {
+        let mut cfg = ControlFlowGraph {
+            vars: Vec::new(),
+            bb: Vec::new(),
+            current: 0,
+            writes_contract_storage: false,
+        };
+
+        cfg.new_basic_block("entry".to_string());
+
+        cfg
+    }
+
+    pub fn new_basic_block(&mut self, name: String) -> usize {
+        let pos = self.bb.len();
+
+        self.bb.push(BasicBlock {
+            name,
+            instr: Vec::new(),
+            phis: None,
+        });
+
+        pos
+    }
+
+    pub fn set_phis(&mut self, bb: usize, phis: HashSet<usize>) {
+        if !phis.is_empty() {
+            self.bb[bb].phis = Some(phis);
+        }
+    }
+
+    pub fn set_basic_block(&mut self, pos: usize) {
+        self.current = pos;
+    }
+
+    pub fn add(&mut self, vartab: &mut Vartable, ins: Instr) {
+        if let Instr::Set { res, .. } = ins {
+            vartab.set_dirty(res);
+        }
+        self.bb[self.current].add(ins);
+    }
+
+    pub fn expr_to_string(&self, contract: &Contract, ns: &Namespace, expr: &Expression) -> String {
+        match expr {
+            Expression::FunctionArg(_, _, pos) => format!("(arg #{})", pos),
+            Expression::BoolLiteral(_, false) => "false".to_string(),
+            Expression::BoolLiteral(_, true) => "true".to_string(),
+            Expression::BytesLiteral(_, _, s) => format!("hex\"{}\"", hex::encode(s)),
+            Expression::NumberLiteral(_, ty, n) => {
+                format!("{} {}", ty.to_string(ns), n.to_str_radix(10))
+            }
+            Expression::StructLiteral(_, _, expr) => format!(
+                "struct {{ {} }}",
+                expr.iter()
+                    .map(|e| self.expr_to_string(contract, ns, e))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::ConstArrayLiteral(_, _, dims, exprs) => format!(
+                "constant {} [ {} ]",
+                dims.iter().map(|d| format!("[{}]", d)).collect::<String>(),
+                exprs
+                    .iter()
+                    .map(|e| self.expr_to_string(contract, ns, e))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::ArrayLiteral(_, _, dims, exprs) => format!(
+                "{} [ {} ]",
+                dims.iter().map(|d| format!("[{}]", d)).collect::<String>(),
+                exprs
+                    .iter()
+                    .map(|e| self.expr_to_string(contract, ns, e))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::Add(_, _, l, r) => format!(
+                "({} + {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Subtract(_, _, l, r) => format!(
+                "({} - {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::BitwiseOr(_, _, l, r) => format!(
+                "({} | {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::BitwiseAnd(_, _, l, r) => format!(
+                "({} & {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::BitwiseXor(_, _, l, r) => format!(
+                "({} ^ {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::ShiftLeft(_, _, l, r) => format!(
+                "({} << {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::ShiftRight(_, _, l, r, _) => format!(
+                "({} >> {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Multiply(_, _, l, r) => format!(
+                "({} * {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::UDivide(_, _, l, r) | Expression::SDivide(_, _, l, r) => format!(
+                "({} / {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::UModulo(_, _, l, r) | Expression::SModulo(_, _, l, r) => format!(
+                "({} % {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Power(_, _, l, r) => format!(
+                "({} ** {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Variable(_, _, res) => format!("%{}", self.vars[*res].id.name),
+            Expression::ConstantVariable(_, _, res) | Expression::StorageVariable(_, _, res) => {
+                format!("${}", contract.variables[*res].name)
+            }
+            Expression::Load(_, _, expr) => {
+                format!("(load {})", self.expr_to_string(contract, ns, expr))
+            }
+            Expression::StorageLoad(_, ty, expr) => format!(
+                "({} storage[{}])",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, expr)
+            ),
+            Expression::ZeroExt(_, ty, e) => format!(
+                "(zext {} {})",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, e)
+            ),
+            Expression::SignExt(_, ty, e) => format!(
+                "(sext {} {})",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, e)
+            ),
+            Expression::Trunc(_, ty, e) => format!(
+                "(trunc {} {})",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, e)
+            ),
+            Expression::SMore(_, l, r) => format!(
+                "({} >(s) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::SLess(_, l, r) => format!(
+                "({} <(s) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::SMoreEqual(_, l, r) => format!(
+                "({} >=(s) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::SLessEqual(_, l, r) => format!(
+                "({} <=(s) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::UMore(_, l, r) => format!(
+                "({} >(u) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::ULess(_, l, r) => format!(
+                "({} <(u) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::UMoreEqual(_, l, r) => format!(
+                "({} >=(u) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::ULessEqual(_, l, r) => format!(
+                "({} <=(u) {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Equal(_, l, r) => format!(
+                "({} == {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::NotEqual(_, l, r) => format!(
+                "({} != {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::ArraySubscript(_, _, a, i) => format!(
+                "(array index {}[{}])",
+                self.expr_to_string(contract, ns, a),
+                self.expr_to_string(contract, ns, i)
+            ),
+            Expression::DynamicArraySubscript(_, _, a, i) => format!(
+                "(darray index {}[{}])",
+                self.expr_to_string(contract, ns, a),
+                self.expr_to_string(contract, ns, i)
+            ),
+            Expression::StorageBytesSubscript(_, a, i) => format!(
+                "(storage bytes index {}[{}])",
+                self.expr_to_string(contract, ns, a),
+                self.expr_to_string(contract, ns, i)
+            ),
+            Expression::StorageBytesPush(_, a, i) => format!(
+                "(storage bytes push {} {})",
+                self.expr_to_string(contract, ns, a),
+                self.expr_to_string(contract, ns, i)
+            ),
+            Expression::StorageBytesPop(_, a) => format!(
+                "(storage bytes pop {})",
+                self.expr_to_string(contract, ns, a),
+            ),
+            Expression::StorageBytesLength(_, a) => format!(
+                "(storage bytes length {})",
+                self.expr_to_string(contract, ns, a),
+            ),
+            Expression::StructMember(_, _, a, f) => format!(
+                "(struct {} field {})",
+                self.expr_to_string(contract, ns, a),
+                f
+            ),
+            Expression::Or(_, l, r) => format!(
+                "({} || {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::And(_, l, r) => format!(
+                "({} && {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Ternary(_, _, c, l, r) => format!(
+                "({} ? {} : {})",
+                self.expr_to_string(contract, ns, c),
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::Not(_, e) => format!("!{}", self.expr_to_string(contract, ns, e)),
+            Expression::Complement(_, _, e) => format!("~{}", self.expr_to_string(contract, ns, e)),
+            Expression::UnaryMinus(_, _, e) => format!("-{}", self.expr_to_string(contract, ns, e)),
+            Expression::Poison => "☠".to_string(),
+            Expression::AllocDynamicArray(_, ty, size, None) => format!(
+                "(alloc {} len {})",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, size)
+            ),
+            Expression::AllocDynamicArray(_, ty, size, Some(init)) => format!(
+                "(alloc {} {} {})",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, size),
+                match str::from_utf8(init) {
+                    Ok(s) => format!("\"{}\"", s.escape_debug()),
+                    Err(_) => format!("hex\"{}\"", hex::encode(init)),
+                }
+            ),
+            Expression::DynamicArrayLength(_, a) => {
+                format!("(darray {} len)", self.expr_to_string(contract, ns, a))
+            }
+            Expression::StringCompare(_, l, r) => format!(
+                "(strcmp ({}) ({}))",
+                self.location_to_string(contract, ns, l),
+                self.location_to_string(contract, ns, r)
+            ),
+            Expression::StringConcat(_, _, l, r) => format!(
+                "(concat ({}) ({}))",
+                self.location_to_string(contract, ns, l),
+                self.location_to_string(contract, ns, r)
+            ),
+            Expression::Keccak256(_, _, exprs) => format!(
+                "(keccak256 {})",
+                exprs
+                    .iter()
+                    .map(|e| self.expr_to_string(contract, ns, &e))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::InternalFunctionCall(_, _, f, args) => format!(
+                "(call {} ({})",
+                contract.functions[*f].name,
+                args.iter()
+                    .map(|a| self.expr_to_string(contract, ns, &a))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::Constructor {
+                contract_no,
+                constructor_no,
+                args,
+                ..
+            } => format!(
+                "(constructor:{} ({}) ({})",
+                ns.contracts[*contract_no].name,
+                ns.contracts[*contract_no].functions[*constructor_no].signature,
+                args.iter()
+                    .map(|a| self.expr_to_string(contract, ns, &a))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::CodeLiteral(_, contract_no, runtime) => format!(
+                "({} code contract {})",
+                if *runtime {
+                    "runtimeCode"
+                } else {
+                    "creationCode"
+                },
+                ns.contracts[*contract_no].name,
+            ),
+            Expression::ExternalFunctionCall {
+                function_no,
+                contract_no,
+                address,
+                args,
+                ..
+            } => format!(
+                "(external call address:{} {}.{} ({})",
+                self.expr_to_string(contract, ns, address),
+                ns.contracts[*contract_no].name,
+                contract.functions[*function_no].name,
+                args.iter()
+                    .map(|a| self.expr_to_string(contract, ns, &a))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Expression::ReturnData(_) => "(external call return data)".to_string(),
+            Expression::GetAddress(_, _) => "(get adddress)".to_string(),
+            Expression::Balance(_, _, addr) => {
+                format!("(balance {})", self.expr_to_string(contract, ns, addr))
+            }
+            Expression::Assign(_, _, l, r) => format!(
+                "{} = {}",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::PostDecrement(_, _, e) => {
+                format!("{}--", self.expr_to_string(contract, ns, e),)
+            }
+            Expression::PostIncrement(_, _, e) => {
+                format!("{}++", self.expr_to_string(contract, ns, e),)
+            }
+            Expression::PreDecrement(_, _, e) => {
+                format!("--{}", self.expr_to_string(contract, ns, e),)
+            }
+            Expression::PreIncrement(_, _, e) => {
+                format!("++{}", self.expr_to_string(contract, ns, e),)
+            }
+            Expression::Cast(_, ty, e) => format!(
+                "{}({})",
+                ty.to_string(ns),
+                self.expr_to_string(contract, ns, e)
+            ),
+            Expression::Builtin(_, _, builtin, args) =>
+                format!("(builtin {:?} ({}))", builtin,                
+                     args.iter().map(|a| self.expr_to_string(contract, ns, &a)).collect::<Vec<String>>().join(", ")
+            )
+            ,
+            // FIXME BEFORE MERGE
+            _ => panic!("{:?}", expr),
+        }
+    }
+
+    fn location_to_string(
+        &self,
+        contract: &Contract,
+        ns: &Namespace,
+        l: &StringLocation,
+    ) -> String {
+        match l {
+            StringLocation::RunTime(e) => self.expr_to_string(contract, ns, e),
+            StringLocation::CompileTime(literal) => match str::from_utf8(literal) {
+                Ok(s) => format!("\"{}\"", s.to_owned()),
+                Err(_) => format!("hex\"{}\"", hex::encode(literal)),
+            },
+        }
+    }
+
+    pub fn instr_to_string(&self, contract: &Contract, ns: &Namespace, instr: &Instr) -> String {
+        match instr {
+            Instr::Return { value } => format!(
+                "return {}",
+                value
+                    .iter()
+                    .map(|expr| self.expr_to_string(contract, ns, expr))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Instr::Set { res, expr } => format!(
+                "ty:{} %{} = {}",
+                self.vars[*res].ty.to_string(ns),
+                self.vars[*res].id.name,
+                self.expr_to_string(contract, ns, expr)
+            ),
+            Instr::Eval { expr } => format!("_ = {}", self.expr_to_string(contract, ns, expr)),
+            Instr::Constant { res, constant } => format!(
+                "%{} = const {}",
+                self.vars[*res].id.name,
+                self.expr_to_string(
+                    contract,
+                    ns,
+                    &contract.variables[*constant].initializer.as_ref().unwrap()
+                )
+            ),
+            Instr::Branch { bb } => format!("branch bb{}", bb),
+            Instr::BranchCond {
+                cond,
+                true_,
+                false_,
+            } => format!(
+                "branchcond {}, bb{}, bb{}",
+                self.expr_to_string(contract, ns, cond),
+                true_,
+                false_
+            ),
+            Instr::ClearStorage { ty, storage } => format!(
+                "clear storage slot({}) ty:{}",
+                self.expr_to_string(contract, ns, storage),
+                ty.to_string(ns),
+            ),
+            Instr::SetStorage { ty, local, storage } => format!(
+                "set storage slot({}) ty:{} = %{}",
+                self.expr_to_string(contract, ns, storage),
+                ty.to_string(ns),
+                self.vars[*local].id.name
+            ),
+            Instr::SetStorageBytes {
+                local,
+                storage,
+                offset,
+            } => format!(
+                "set storage slot({}) offset:{} = %{}",
+                self.expr_to_string(contract, ns, storage),
+                self.expr_to_string(contract, ns, offset),
+                self.vars[*local].id.name
+            ),
+            Instr::AssertFailure { expr: None } => "assert-failure".to_string(),
+            Instr::AssertFailure { expr: Some(expr) } => {
+                format!("assert-failure:{}", self.expr_to_string(contract, ns, expr))
+            }
+            Instr::Call { res, func, args } => format!(
+                "{} = call {} {} {}",
+                res.iter()
+                    .map(|local| format!("%{}", self.vars[*local].id.name))
+                    .collect::<Vec<String>>()
+                    .join(", "),
+                *func,
+                contract.functions[*func].name.to_owned(),
+                args.iter()
+                    .map(|expr| self.expr_to_string(contract, ns, expr))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Instr::ExternalCall {
+                success,
+                address,
+                contract_no: Some(contract_no),
+                function_no,
+                args,
+                value,
+                gas,
+            } => format!(
+                "{} = external call address:{} signature:{} value:{} gas:{} func:{}.{} {}",
+                match success {
+                    Some(i) => format!("%{}", self.vars[*i].id.name),
+                    None => "_".to_string(),
+                },
+                self.expr_to_string(contract, ns, address),
+                ns.contracts[*contract_no].functions[*function_no].signature,
+                self.expr_to_string(contract, ns, value),
+                self.expr_to_string(contract, ns, gas),
+                ns.contracts[*contract_no].name,
+                ns.contracts[*contract_no].functions[*function_no].name,
+                args.iter()
+                    .map(|expr| self.expr_to_string(contract, ns, expr))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Instr::ExternalCall {
+                success,
+                address,
+                contract_no: None,
+                value,
+                ..
+            } => format!(
+                "{} = external call address:{} value:{}",
+                match success {
+                    Some(i) => format!("%{}", self.vars[*i].id.name),
+                    None => "_".to_string(),
+                },
+                self.expr_to_string(contract, ns, address),
+                self.expr_to_string(contract, ns, value),
+            ),
+            Instr::AbiDecode {
+                res,
+                tys,
+                selector,
+                exception,
+                data,
+            } => format!(
+                "{} = (abidecode:(%{}, {} {} ({}))",
+                res.iter()
+                    .map(|local| format!("%{}", self.vars[*local].id.name))
+                    .collect::<Vec<String>>()
+                    .join(", "),
+                self.expr_to_string(contract, ns, data),
+                selector
+                    .iter()
+                    .map(|s| format!("selector:0x{:08x} ", s))
+                    .collect::<String>(),
+                exception
+                    .iter()
+                    .map(|bb| format!("exception:bb{} ", bb))
+                    .collect::<String>(),
+                tys.iter()
+                    .map(|ty| ty.ty.to_string(ns))
+                    .collect::<Vec<String>>()
+                    .join(", "),
+            ),
+            Instr::Store { dest, pos } => format!(
+                "store {}, {}",
+                self.expr_to_string(contract, ns, dest),
+                self.vars[*pos].id.name
+            ),
+            Instr::Print { expr } => format!("print {}", self.expr_to_string(contract, ns, expr)),
+            Instr::Constructor {
+                success,
+                res,
+                contract_no,
+                constructor_no,
+                args,
+                gas,
+                salt,
+                value,
+            } => format!(
+                "%{}, {} = constructor salt:{} value:{} gas:{} {} #{} ({})",
+                self.vars[*res].id.name,
+                match success {
+                    Some(i) => format!("%{}", self.vars[*i].id.name),
+                    None => "_".to_string(),
+                },
+                match salt {
+                    Some(salt) => self.expr_to_string(contract, ns, salt),
+                    None => "".to_string(),
+                },
+                match value {
+                    Some(value) => self.expr_to_string(contract, ns, value),
+                    None => "".to_string(),
+                },
+                self.expr_to_string(contract, ns, gas),
+                ns.contracts[*contract_no].name,
+                constructor_no,
+                args.iter()
+                    .map(|expr| self.expr_to_string(contract, ns, expr))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Instr::Unreachable => "unreachable".to_string(),
+            Instr::SelfDestruct { recipient } => format!(
+                "selfdestruct {}",
+                self.expr_to_string(contract, ns, recipient)
+            ),
+            Instr::Hash { res, hash, expr } => format!(
+                "%{} = hash {} {}",
+                self.vars[*res].id.name,
+                hash,
+                self.expr_to_string(contract, ns, expr)
+            ),
+        }
+    }
+
+    pub fn basic_block_to_string(&self, contract: &Contract, ns: &Namespace, pos: usize) -> String {
+        let mut s = format!("bb{}: # {}\n", pos, self.bb[pos].name);
+
+        if let Some(ref phis) = self.bb[pos].phis {
+            s.push_str("# phis: ");
+            let mut first = true;
+            for p in phis {
+                if !first {
+                    s.push_str(", ");
+                }
+                first = false;
+                s.push_str(&self.vars[*p].id.name);
+            }
+            s.push_str("\n");
+        }
+
+        for ins in &self.bb[pos].instr {
+            s.push_str(&format!("\t{}\n", self.instr_to_string(contract, ns, ins)));
+        }
+
+        s
+    }
+
+    pub fn to_string(&self, contract: &Contract, ns: &Namespace) -> String {
+        let mut s = String::from("");
+
+        for i in 0..self.bb.len() {
+            s.push_str(&self.basic_block_to_string(contract, ns, i));
+        }
+
+        s
+    }
+}
+
+pub fn generate_cfg(contract_no: usize, function_no: usize, ns: &Namespace) -> ControlFlowGraph {
+    let mut cfg = ControlFlowGraph::new();
+
+    let mut vartab =
+        Vartable::new_with_syms(&ns.contracts[contract_no].functions[function_no].symtable);
+    let mut loops = LoopScopes::new();
+
+    let func = &ns.contracts[contract_no].functions[function_no];
+
+    // populate the argument variables
+    for (i, arg) in func.symtable.arguments.iter().enumerate() {
+        if let Some(pos) = arg {
+            let var = &func.symtable.vars[*pos];
+            cfg.add(
+                &mut vartab,
+                Instr::Set {
+                    res: *pos,
+                    expr: Expression::FunctionArg(var.id.loc, var.ty.clone(), i),
+                },
+            );
+        }
+    }
+
+    // named returns should be populated
+    for (i, pos) in func.symtable.returns.iter().enumerate() {
+        if !func.returns[i].name.is_empty() {
+            cfg.add(
+                &mut vartab,
+                Instr::Set {
+                    res: *pos,
+                    expr: func.returns[i].ty.default(ns),
+                },
+            );
+        }
+    }
+
+    for stmt in &func.body {
+        statement(
+            stmt,
+            func,
+            &mut cfg,
+            contract_no,
+            ns,
+            &mut vartab,
+            &mut loops,
+        );
+    }
+
+    cfg.vars = vartab.drain();
+
+    // walk cfg to check for use for before initialize
+    cfg
+}
+
+#[derive(Clone)]
+pub enum Storage {
+    Constant(usize),
+    Contract(BigInt),
+    Local,
+}
+
+#[derive(Clone)]
+pub struct Variable {
+    pub id: pt::Identifier,
+    pub ty: Type,
+    pub pos: usize,
+    pub storage: Storage,
+}
+
+#[derive(Default)]
+pub struct Vartable {
+    vars: Vec<Variable>,
+    dirty: Vec<DirtyTracker>,
+}
+
+pub struct DirtyTracker {
+    lim: usize,
+    set: HashSet<usize>,
+}
+
+impl Vartable {
+    pub fn new_with_syms(sym: &Symtable) -> Self {
+        let vars = sym
+            .vars
+            .iter()
+            .map(|v| Variable {
+                id: v.id.clone(),
+                ty: v.ty.clone(),
+                pos: v.pos,
+                storage: Storage::Local,
+            })
+            .collect();
+
+        Vartable {
+            vars,
+            dirty: Vec::new(),
+        }
+    }
+
+    pub fn new() -> Self {
+        Vartable {
+            vars: Vec::new(),
+            dirty: Vec::new(),
+        }
+    }
+
+    pub fn add(&mut self, id: &pt::Identifier, ty: Type) -> Option<usize> {
+        let pos = self.vars.len();
+
+        self.vars.push(Variable {
+            id: id.clone(),
+            ty,
+            pos,
+            storage: Storage::Local,
+        });
+
+        Some(pos)
+    }
+
+    pub fn temp_anonymous(&mut self, ty: &Type) -> usize {
+        let pos = self.vars.len();
+
+        self.vars.push(Variable {
+            id: pt::Identifier {
+                name: format!("temp.{}", pos),
+                loc: pt::Loc(0, 0),
+            },
+            ty: ty.clone(),
+            pos,
+            storage: Storage::Local,
+        });
+
+        pos
+    }
+
+    pub fn temp(&mut self, id: &pt::Identifier, ty: &Type) -> usize {
+        let pos = self.vars.len();
+
+        self.vars.push(Variable {
+            id: pt::Identifier {
+                name: format!("{}.temp.{}", id.name, pos),
+                loc: id.loc,
+            },
+            ty: ty.clone(),
+            pos,
+            storage: Storage::Local,
+        });
+
+        pos
+    }
+
+    pub fn temp_name(&mut self, name: &str, ty: &Type) -> usize {
+        let pos = self.vars.len();
+
+        self.vars.push(Variable {
+            id: pt::Identifier {
+                name: format!("{}.temp.{}", name, pos),
+                loc: pt::Loc(0, 0),
+            },
+            ty: ty.clone(),
+            pos,
+            storage: Storage::Local,
+        });
+
+        pos
+    }
+
+    pub fn drain(self) -> Vec<Variable> {
+        self.vars
+    }
+
+    // In order to create phi nodes, we need to track what vars are set in a certain scope
+    pub fn set_dirty(&mut self, pos: usize) {
+        for e in &mut self.dirty {
+            if pos < e.lim {
+                e.set.insert(pos);
+            }
+        }
+    }
+
+    pub fn new_dirty_tracker(&mut self) {
+        self.dirty.push(DirtyTracker {
+            lim: self.vars.len(),
+            set: HashSet::new(),
+        });
+    }
+
+    pub fn pop_dirty_tracker(&mut self) -> HashSet<usize> {
+        self.dirty.pop().unwrap().set
+    }
+}

+ 1127 - 0
src/codegen/expression.rs

@@ -0,0 +1,1127 @@
+use super::cfg::{ControlFlowGraph, HashTy, Instr, Vartable};
+use super::storage::{array_offset, array_pop, array_push, bytes_pop, bytes_push};
+use num_bigint::BigInt;
+use num_traits::FromPrimitive;
+use num_traits::One;
+use num_traits::ToPrimitive;
+use num_traits::Zero;
+use parser::pt;
+use sema::ast::{Builtin, Expression, Namespace, StringLocation, Type};
+use sema::eval::eval_const_number;
+use sema::expression::{cast_shift_arg, try_bigint_to_expression, try_cast};
+use std::collections::HashSet;
+use std::ops::Mul;
+
+pub fn expression(
+    expr: &Expression,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    match expr {
+        Expression::StorageVariable(_, _, var_no) => {
+            ns.contracts[contract_no].variables[*var_no].get_storage_slot()
+        }
+        Expression::StorageLoad(loc, ty, expr) => Expression::StorageLoad(
+            *loc,
+            ty.clone(),
+            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Add(loc, ty, left, right) => Expression::Add(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Subtract(loc, ty, left, right) => Expression::Subtract(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Multiply(loc, ty, left, right) => Expression::Multiply(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SDivide(loc, ty, left, right) => Expression::SDivide(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::UDivide(loc, ty, left, right) => Expression::UDivide(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SModulo(loc, ty, left, right) => Expression::SModulo(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::UModulo(loc, ty, left, right) => Expression::UModulo(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Power(loc, ty, left, right) => Expression::Power(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::BitwiseOr(loc, ty, left, right) => Expression::BitwiseOr(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::BitwiseAnd(loc, ty, left, right) => Expression::BitwiseAnd(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::BitwiseXor(loc, ty, left, right) => Expression::BitwiseXor(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::ShiftLeft(loc, ty, left, right) => Expression::ShiftLeft(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::ShiftRight(loc, ty, left, right, sign) => Expression::ShiftRight(
+            *loc,
+            ty.clone(),
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+            *sign,
+        ),
+        Expression::Equal(loc, left, right) => Expression::Equal(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::NotEqual(loc, left, right) => Expression::NotEqual(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SMore(loc, left, right) => Expression::SMore(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SMoreEqual(loc, left, right) => Expression::SMoreEqual(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::UMore(loc, left, right) => Expression::UMore(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::UMoreEqual(loc, left, right) => Expression::UMoreEqual(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SLess(loc, left, right) => Expression::SLess(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SLessEqual(loc, left, right) => Expression::SLessEqual(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::ULess(loc, left, right) => Expression::ULess(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::ULessEqual(loc, left, right) => Expression::ULessEqual(
+            *loc,
+            Box::new(expression(left, cfg, contract_no, ns, vartab)),
+            Box::new(expression(right, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::ConstantVariable(_, _, no) => expression(
+            ns.contracts[contract_no].variables[*no]
+                .initializer
+                .as_ref()
+                .unwrap(),
+            cfg,
+            contract_no,
+            ns,
+            vartab,
+        ),
+        Expression::Not(loc, expr) => Expression::Not(
+            *loc,
+            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Complement(loc, ty, expr) => Expression::Complement(
+            *loc,
+            ty.clone(),
+            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::UnaryMinus(loc, ty, expr) => Expression::UnaryMinus(
+            *loc,
+            ty.clone(),
+            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::StructLiteral(loc, ty, exprs) => Expression::StructLiteral(
+            *loc,
+            ty.clone(),
+            exprs
+                .iter()
+                .map(|e| expression(e, cfg, contract_no, ns, vartab))
+                .collect(),
+        ),
+        Expression::Assign(_, _, left, right) => {
+            assign_single(left, right, cfg, contract_no, ns, vartab)
+        }
+        Expression::PreDecrement(loc, ty, var) | Expression::PreIncrement(loc, ty, var) => {
+            let res = vartab.temp_anonymous(ty);
+            let v = expression(var, cfg, contract_no, ns, vartab);
+            let v = match var.ty() {
+                Type::Ref(ty) => Expression::Load(var.loc(), ty.as_ref().clone(), Box::new(v)),
+                Type::StorageRef(ty) => {
+                    Expression::StorageLoad(var.loc(), ty.as_ref().clone(), Box::new(v))
+                }
+                _ => v,
+            };
+
+            let one = Box::new(Expression::NumberLiteral(*loc, ty.clone(), BigInt::one()));
+            let expr = match expr {
+                Expression::PreDecrement(_, _, _) => {
+                    Expression::Subtract(*loc, ty.clone(), Box::new(v), one)
+                }
+                Expression::PreIncrement(_, _, _) => {
+                    Expression::Add(*loc, ty.clone(), Box::new(v), one)
+                }
+                _ => unreachable!(),
+            };
+
+            cfg.add(vartab, Instr::Set { res, expr });
+
+            match var.as_ref() {
+                Expression::Variable(_, _, pos) => {
+                    cfg.add(
+                        vartab,
+                        Instr::Set {
+                            res: *pos,
+                            expr: Expression::Variable(*loc, ty.clone(), res),
+                        },
+                    );
+                }
+                _ => {
+                    let dest = expression(var, cfg, contract_no, ns, vartab);
+
+                    match var.ty() {
+                        Type::StorageRef(_) => {
+                            cfg.add(
+                                vartab,
+                                Instr::SetStorage {
+                                    local: res,
+                                    ty: ty.clone(),
+                                    storage: dest,
+                                },
+                            );
+                        }
+                        Type::Ref(_) => {
+                            cfg.add(vartab, Instr::Store { pos: res, dest });
+                        }
+                        _ => unreachable!(),
+                    }
+                }
+            }
+
+            Expression::Variable(*loc, ty.clone(), res)
+        }
+        Expression::PostDecrement(loc, ty, var) | Expression::PostIncrement(loc, ty, var) => {
+            let res = vartab.temp_anonymous(ty);
+            let v = expression(var, cfg, contract_no, ns, vartab);
+            let v = match var.ty() {
+                Type::Ref(ty) => Expression::Load(var.loc(), ty.as_ref().clone(), Box::new(v)),
+                Type::StorageRef(ty) => {
+                    Expression::StorageLoad(var.loc(), ty.as_ref().clone(), Box::new(v))
+                }
+                _ => v,
+            };
+
+            cfg.add(vartab, Instr::Set { res, expr: v });
+
+            let one = Box::new(Expression::NumberLiteral(*loc, ty.clone(), BigInt::one()));
+            let expr = match expr {
+                Expression::PostDecrement(_, _, _) => Expression::Subtract(
+                    *loc,
+                    ty.clone(),
+                    Box::new(Expression::Variable(*loc, ty.clone(), res)),
+                    one,
+                ),
+                Expression::PostIncrement(_, _, _) => Expression::Add(
+                    *loc,
+                    ty.clone(),
+                    Box::new(Expression::Variable(*loc, ty.clone(), res)),
+                    one,
+                ),
+                _ => unreachable!(),
+            };
+
+            match var.as_ref() {
+                Expression::Variable(_, _, pos) => {
+                    cfg.add(vartab, Instr::Set { res: *pos, expr });
+                }
+                _ => {
+                    let dest = expression(var, cfg, contract_no, ns, vartab);
+                    let res = vartab.temp_anonymous(ty);
+                    cfg.add(vartab, Instr::Set { res, expr });
+
+                    match var.ty() {
+                        Type::StorageRef(_) => {
+                            cfg.add(
+                                vartab,
+                                Instr::SetStorage {
+                                    local: res,
+                                    ty: ty.clone(),
+                                    storage: dest,
+                                },
+                            );
+                        }
+                        Type::Ref(_) => {
+                            cfg.add(vartab, Instr::Store { pos: res, dest });
+                        }
+                        _ => unreachable!(),
+                    }
+                }
+            }
+            Expression::Variable(*loc, ty.clone(), res)
+        }
+        Expression::Constructor {
+            loc,
+            contract_no,
+            constructor_no,
+            args,
+            value,
+            gas,
+            salt,
+        } => {
+            let address_res = vartab.temp_anonymous(&Type::Contract(*contract_no));
+
+            let args = args
+                .iter()
+                .map(|v| expression(&v, cfg, *contract_no, ns, vartab))
+                .collect();
+            let gas = expression(gas, cfg, *contract_no, ns, vartab);
+            let value = match value {
+                Some(value) => Some(expression(&value, cfg, *contract_no, ns, vartab)),
+                None => None,
+            };
+            let salt = match salt {
+                Some(salt) => Some(expression(&salt, cfg, *contract_no, ns, vartab)),
+                None => None,
+            };
+
+            cfg.add(
+                vartab,
+                Instr::Constructor {
+                    success: None,
+                    res: address_res,
+                    contract_no: *contract_no,
+                    constructor_no: *constructor_no,
+                    args,
+                    value,
+                    gas,
+                    salt,
+                },
+            );
+
+            Expression::Variable(*loc, Type::Contract(*contract_no), address_res)
+        }
+        Expression::InternalFunctionCall(_, _, _, _) | Expression::ExternalFunctionCall { .. } => {
+            let mut returns = emit_function_call(expr, contract_no, cfg, ns, vartab);
+
+            assert_eq!(returns.len(), 1);
+
+            returns.remove(0)
+        }
+        Expression::ArraySubscript(loc, ty, array, index) => {
+            array_subscript(loc, ty, array, index, cfg, contract_no, ns, vartab)
+        }
+        Expression::StructMember(loc, ty, var, member) => Expression::StructMember(
+            *loc,
+            ty.clone(),
+            Box::new(expression(var, cfg, contract_no, ns, vartab)),
+            *member,
+        ),
+        Expression::StorageBytesSubscript(loc, var, index) => Expression::StorageBytesSubscript(
+            *loc,
+            Box::new(expression(var, cfg, contract_no, ns, vartab)),
+            Box::new(expression(index, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::StringCompare(loc, left, right) => Expression::StringCompare(
+            *loc,
+            string_location(left, cfg, contract_no, ns, vartab),
+            string_location(right, cfg, contract_no, ns, vartab),
+        ),
+        Expression::StringConcat(loc, ty, left, right) => Expression::StringConcat(
+            *loc,
+            ty.clone(),
+            string_location(left, cfg, contract_no, ns, vartab),
+            string_location(right, cfg, contract_no, ns, vartab),
+        ),
+        Expression::DynamicArrayLength(loc, expr) => Expression::DynamicArrayLength(
+            *loc,
+            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::StorageBytesLength(loc, expr) => Expression::StorageBytesLength(
+            *loc,
+            Box::new(expression(expr, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Or(loc, left, right) => {
+            let boolty = Type::Bool;
+            let l = expression(left, cfg, contract_no, ns, vartab);
+
+            let pos = vartab.temp(
+                &pt::Identifier {
+                    name: "or".to_owned(),
+                    loc: *loc,
+                },
+                &Type::Bool,
+            );
+
+            let right_side = cfg.new_basic_block("or_right_side".to_string());
+            let end_or = cfg.new_basic_block("or_end".to_string());
+
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    res: pos,
+                    expr: Expression::BoolLiteral(*loc, true),
+                },
+            );
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: l,
+                    true_: end_or,
+                    false_: right_side,
+                },
+            );
+            cfg.set_basic_block(right_side);
+
+            let r = expression(right, cfg, contract_no, ns, vartab);
+
+            cfg.add(vartab, Instr::Set { res: pos, expr: r });
+
+            let mut phis = HashSet::new();
+            phis.insert(pos);
+
+            cfg.set_phis(end_or, phis);
+
+            cfg.add(vartab, Instr::Branch { bb: end_or });
+
+            cfg.set_basic_block(end_or);
+
+            Expression::Variable(*loc, boolty, pos)
+        }
+        Expression::And(loc, left, right) => {
+            let boolty = Type::Bool;
+            let l = expression(left, cfg, contract_no, ns, vartab);
+
+            let pos = vartab.temp(
+                &pt::Identifier {
+                    name: "and".to_owned(),
+                    loc: *loc,
+                },
+                &Type::Bool,
+            );
+
+            let right_side = cfg.new_basic_block("and_right_side".to_string());
+            let end_and = cfg.new_basic_block("and_end".to_string());
+
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    res: pos,
+                    expr: Expression::BoolLiteral(*loc, false),
+                },
+            );
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: l,
+                    true_: right_side,
+                    false_: end_and,
+                },
+            );
+            cfg.set_basic_block(right_side);
+
+            let r = expression(right, cfg, contract_no, ns, vartab);
+
+            cfg.add(vartab, Instr::Set { res: pos, expr: r });
+
+            let mut phis = HashSet::new();
+            phis.insert(pos);
+
+            cfg.set_phis(end_and, phis);
+
+            cfg.add(vartab, Instr::Branch { bb: end_and });
+
+            cfg.set_basic_block(end_and);
+
+            Expression::Variable(*loc, boolty, pos)
+        }
+        Expression::Trunc(loc, ty, e) => Expression::Trunc(
+            *loc,
+            ty.clone(),
+            Box::new(expression(e, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::ZeroExt(loc, ty, e) => Expression::ZeroExt(
+            *loc,
+            ty.clone(),
+            Box::new(expression(e, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::SignExt(loc, ty, e) => Expression::SignExt(
+            *loc,
+            ty.clone(),
+            Box::new(expression(e, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Cast(loc, ty, e) => Expression::Cast(
+            *loc,
+            ty.clone(),
+            Box::new(expression(e, cfg, contract_no, ns, vartab)),
+        ),
+        Expression::Load(loc, ty, e) => Expression::Load(
+            *loc,
+            ty.clone(),
+            Box::new(expression(e, cfg, contract_no, ns, vartab)),
+        ),
+        // for some built-ins, we have to inline special case code
+        Expression::Builtin(loc, _, Builtin::ArrayPush, args) => {
+            array_push(loc, args, cfg, contract_no, ns, vartab)
+        }
+        Expression::Builtin(loc, _, Builtin::ArrayPop, args) => {
+            array_pop(loc, args, cfg, contract_no, ns, vartab)
+        }
+        Expression::Builtin(loc, _, Builtin::BytesPush, args) => {
+            bytes_push(loc, args, cfg, contract_no, ns, vartab)
+        }
+        Expression::Builtin(loc, _, Builtin::BytesPop, args) => {
+            bytes_pop(loc, args, cfg, contract_no, ns, vartab)
+        }
+        Expression::Builtin(_, _, Builtin::Assert, args) => {
+            let true_ = cfg.new_basic_block("noassert".to_owned());
+            let false_ = cfg.new_basic_block("doassert".to_owned());
+
+            let cond = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond,
+                    true_,
+                    false_,
+                },
+            );
+
+            cfg.set_basic_block(false_);
+            cfg.add(vartab, Instr::AssertFailure { expr: None });
+
+            cfg.set_basic_block(true_);
+
+            Expression::Poison
+        }
+        Expression::Builtin(_, _, Builtin::Print, args) => {
+            let expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(vartab, Instr::Print { expr });
+
+            Expression::Poison
+        }
+        Expression::Builtin(_, _, Builtin::Require, args) => {
+            let true_ = cfg.new_basic_block("noassert".to_owned());
+            let false_ = cfg.new_basic_block("doassert".to_owned());
+
+            let cond = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond,
+                    true_,
+                    false_,
+                },
+            );
+
+            cfg.set_basic_block(false_);
+
+            let expr = args
+                .get(1)
+                .map(|s| expression(s, cfg, contract_no, ns, vartab));
+
+            cfg.add(vartab, Instr::AssertFailure { expr });
+
+            cfg.set_basic_block(true_);
+
+            Expression::Poison
+        }
+        Expression::Builtin(_, _, Builtin::Revert, args) => {
+            let expr = args
+                .get(0)
+                .map(|s| expression(s, cfg, contract_no, ns, vartab));
+
+            cfg.add(vartab, Instr::AssertFailure { expr });
+
+            Expression::Poison
+        }
+        Expression::Builtin(_, _, Builtin::SelfDestruct, args) => {
+            let recipient = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(vartab, Instr::SelfDestruct { recipient });
+
+            Expression::Poison
+        }
+        Expression::Builtin(loc, tys, Builtin::Keccak256, args) => {
+            let res = vartab.temp_anonymous(&tys[0]);
+            let expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::Hash {
+                    res,
+                    hash: HashTy::Keccak256,
+                    expr,
+                },
+            );
+
+            Expression::Variable(*loc, tys[0].clone(), res)
+        }
+        Expression::Builtin(loc, tys, Builtin::Ripemd160, args) => {
+            let res = vartab.temp_anonymous(&tys[0]);
+            let expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::Hash {
+                    res,
+                    hash: HashTy::Ripemd160,
+                    expr,
+                },
+            );
+
+            Expression::Variable(*loc, tys[0].clone(), res)
+        }
+        Expression::Builtin(loc, tys, Builtin::Sha256, args) => {
+            let res = vartab.temp_anonymous(&tys[0]);
+            let expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::Hash {
+                    res,
+                    hash: HashTy::Sha256,
+                    expr,
+                },
+            );
+
+            Expression::Variable(*loc, tys[0].clone(), res)
+        }
+        Expression::Builtin(loc, tys, Builtin::Blake2_128, args) => {
+            let res = vartab.temp_anonymous(&tys[0]);
+            let expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::Hash {
+                    res,
+                    hash: HashTy::Blake2_128,
+                    expr,
+                },
+            );
+
+            Expression::Variable(*loc, tys[0].clone(), res)
+        }
+        Expression::Builtin(loc, tys, Builtin::Blake2_256, args) => {
+            let res = vartab.temp_anonymous(&tys[0]);
+            let expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::Hash {
+                    res,
+                    hash: HashTy::Blake2_256,
+                    expr,
+                },
+            );
+
+            Expression::Variable(*loc, tys[0].clone(), res)
+        }
+        Expression::Builtin(loc, _, Builtin::PayableSend, args) => {
+            let address = expression(&args[0], cfg, contract_no, ns, vartab);
+            let value = expression(&args[1], cfg, contract_no, ns, vartab);
+
+            let success = vartab.temp(
+                &pt::Identifier {
+                    loc: *loc,
+                    name: "success".to_owned(),
+                },
+                &Type::Bool,
+            );
+
+            cfg.add(
+                vartab,
+                Instr::ExternalCall {
+                    success: Some(success),
+                    address,
+                    contract_no: None,
+                    function_no: 0,
+                    args: Vec::new(),
+                    value,
+                    gas: Expression::NumberLiteral(*loc, Type::Uint(64), BigInt::zero()),
+                },
+            );
+
+            Expression::Variable(*loc, Type::Bool, success)
+        }
+        Expression::Builtin(loc, _, Builtin::PayableTransfer, args) => {
+            let address = expression(&args[0], cfg, contract_no, ns, vartab);
+            let value = expression(&args[1], cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::ExternalCall {
+                    success: None,
+                    address,
+                    contract_no: None,
+                    function_no: 0,
+                    args: Vec::new(),
+                    value,
+                    gas: Expression::NumberLiteral(*loc, Type::Uint(64), BigInt::zero()),
+                },
+            );
+
+            Expression::Poison
+        }
+        _ => expr.clone(),
+    }
+}
+
+pub fn assign_single(
+    left: &Expression,
+    right: &Expression,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    match left {
+        Expression::Variable(_, _, pos) => {
+            let expr = expression(right, cfg, contract_no, ns, vartab);
+            cfg.add(vartab, Instr::Set { res: *pos, expr });
+
+            left.clone()
+        }
+        _ => {
+            let left_ty = left.ty();
+            let ty = left_ty.deref();
+
+            let pos = vartab.temp_anonymous(&ty);
+
+            let dest = expression(left, cfg, contract_no, ns, vartab);
+            let right = expression(right, cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    res: pos,
+                    expr: right,
+                },
+            );
+
+            match left_ty {
+                Type::StorageRef(_) => {
+                    if let Expression::StorageBytesSubscript(_, array, index) = dest {
+                        // Set a byte in a byte array
+                        cfg.add(
+                            vartab,
+                            Instr::SetStorageBytes {
+                                local: pos,
+                                storage: array,
+                                offset: index,
+                            },
+                        );
+                    } else {
+                        cfg.add(
+                            vartab,
+                            Instr::SetStorage {
+                                local: pos,
+                                ty: ty.deref_any().clone(),
+                                storage: dest,
+                            },
+                        );
+                    }
+                }
+                Type::Ref(_) => {
+                    cfg.add(vartab, Instr::Store { pos, dest });
+                }
+                _ => unreachable!(),
+            }
+
+            Expression::Variable(left.loc(), ty.clone(), pos)
+        }
+    }
+}
+
+/// Convert a function call expression to CFG in expression context
+pub fn emit_function_call(
+    expr: &Expression,
+    callee_contract_no: usize,
+    cfg: &mut ControlFlowGraph,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Vec<Expression> {
+    match expr {
+        Expression::InternalFunctionCall(_, _, func, args) => {
+            let args = args
+                .iter()
+                .map(|a| expression(a, cfg, callee_contract_no, ns, vartab))
+                .collect();
+            let ftype = &ns.contracts[callee_contract_no].functions[*func];
+
+            if !ftype.returns.is_empty() {
+                let mut res = Vec::new();
+                let mut returns = Vec::new();
+
+                for ret in &ftype.returns {
+                    let id = pt::Identifier {
+                        loc: ret.loc,
+                        name: ret.name.to_owned(),
+                    };
+
+                    let temp_pos = vartab.temp(&id, &ret.ty);
+                    res.push(temp_pos);
+                    returns.push(Expression::Variable(id.loc, ret.ty.clone(), temp_pos));
+                }
+
+                cfg.add(
+                    vartab,
+                    Instr::Call {
+                        res,
+                        func: *func,
+                        args,
+                    },
+                );
+
+                returns
+            } else {
+                cfg.add(
+                    vartab,
+                    Instr::Call {
+                        res: Vec::new(),
+                        func: *func,
+                        args,
+                    },
+                );
+
+                vec![Expression::Poison]
+            }
+        }
+        Expression::ExternalFunctionCall {
+            loc,
+            contract_no,
+            function_no,
+            address,
+            args,
+            value,
+            gas,
+            ..
+        } => {
+            let ftype = &ns.contracts[*contract_no].functions[*function_no];
+            let args = args
+                .iter()
+                .map(|a| expression(a, cfg, callee_contract_no, ns, vartab))
+                .collect();
+            let address = expression(address, cfg, callee_contract_no, ns, vartab);
+            let gas = expression(gas, cfg, callee_contract_no, ns, vartab);
+            let value = expression(value, cfg, callee_contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::ExternalCall {
+                    success: None,
+                    address,
+                    contract_no: Some(*contract_no),
+                    function_no: *function_no,
+                    args,
+                    value,
+                    gas,
+                },
+            );
+
+            if !ftype.returns.is_empty() {
+                let mut returns = Vec::new();
+                let mut res = Vec::new();
+
+                for ret in &ftype.returns {
+                    let id = pt::Identifier {
+                        loc: ret.loc,
+                        name: ret.name.to_owned(),
+                    };
+                    let temp_pos = vartab.temp(&id, &ret.ty);
+                    res.push(temp_pos);
+                    returns.push(Expression::Variable(id.loc, ret.ty.clone(), temp_pos));
+                }
+
+                cfg.add(
+                    vartab,
+                    Instr::AbiDecode {
+                        res,
+                        selector: None,
+                        exception: None,
+                        tys: ftype.returns.clone(),
+                        data: Expression::ReturnData(*loc),
+                    },
+                );
+
+                returns
+            } else {
+                vec![Expression::Poison]
+            }
+        }
+        _ => unreachable!(),
+    }
+}
+
+/// Resolve an array subscript expression
+fn array_subscript(
+    loc: &pt::Loc,
+    ty: &Type,
+    array: &Expression,
+    index: &Expression,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    if array.ty().is_mapping() {
+        let array = expression(array, cfg, contract_no, ns, vartab);
+        let index = expression(index, cfg, contract_no, ns, vartab);
+        return Expression::Keccak256(*loc, ty.clone(), vec![array, index]);
+    }
+
+    let array_ty = array.ty();
+    let mut array = expression(array, cfg, contract_no, ns, vartab);
+    let index_ty = index.ty();
+    let index = expression(index, cfg, contract_no, ns, vartab);
+    let index_loc = index.loc();
+
+    let index_width = index_ty.bits(ns);
+
+    let array_length = match array_ty.deref_any() {
+        Type::Bytes(n) => try_bigint_to_expression(&array.loc(), &BigInt::from(*n)).unwrap(),
+        Type::Array(_, _) => match array_ty.array_length() {
+            None => {
+                if let Type::StorageRef(_) = array_ty {
+                    let array_length =
+                        Expression::StorageLoad(*loc, Type::Uint(256), Box::new(array.clone()));
+
+                    array = Expression::Keccak256(*loc, Type::Uint(256), vec![array]);
+
+                    array_length
+                } else {
+                    Expression::DynamicArrayLength(*loc, Box::new(array.clone()))
+                }
+            }
+            Some(l) => try_bigint_to_expression(loc, l).unwrap(),
+        },
+        Type::DynamicBytes => Expression::DynamicArrayLength(*loc, Box::new(array.clone())),
+        _ => {
+            unreachable!();
+        }
+    };
+
+    let array_width = array_length.ty().bits(ns);
+    let width = std::cmp::max(array_width, index_width);
+    let coerced_ty = Type::Uint(width);
+
+    let pos = vartab.temp(
+        &pt::Identifier {
+            name: "index".to_owned(),
+            loc: *loc,
+        },
+        &coerced_ty,
+    );
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: pos,
+            expr: try_cast(&index.loc(), index, &coerced_ty, false, ns).unwrap(),
+        },
+    );
+
+    // If the array is fixed length and the index also constant, the
+    // branch will be optimized away.
+    let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
+    let in_bounds = cfg.new_basic_block("in_bounds".to_string());
+
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: Expression::UMoreEqual(
+                *loc,
+                Box::new(Expression::Variable(index_loc, coerced_ty.clone(), pos)),
+                Box::new(
+                    try_cast(&array.loc(), array_length.clone(), &coerced_ty, false, ns).unwrap(),
+                ),
+            ),
+            true_: out_of_bounds,
+            false_: in_bounds,
+        },
+    );
+
+    cfg.set_basic_block(out_of_bounds);
+    cfg.add(vartab, Instr::AssertFailure { expr: None });
+
+    cfg.set_basic_block(in_bounds);
+
+    if let Type::StorageRef(ty) = array_ty {
+        let elem_ty = ty.storage_array_elem();
+        let elem_size = elem_ty.storage_slots(ns);
+        let slot_ty = Type::Uint(256);
+
+        if let Ok(array_length) = eval_const_number(&array_length, Some(contract_no), ns) {
+            if array_length.1.mul(elem_size.clone()).to_u64().is_some() {
+                // we need to calculate the storage offset. If this can be done with 64 bit
+                // arithmetic it will be much more efficient on wasm
+                return Expression::Add(
+                    *loc,
+                    elem_ty,
+                    Box::new(array),
+                    Box::new(Expression::ZeroExt(
+                        *loc,
+                        slot_ty,
+                        Box::new(Expression::Multiply(
+                            *loc,
+                            Type::Uint(64),
+                            Box::new(
+                                try_cast(
+                                    &index_loc,
+                                    Expression::Variable(index_loc, coerced_ty, pos),
+                                    &Type::Uint(64),
+                                    false,
+                                    ns,
+                                )
+                                .unwrap(),
+                            ),
+                            Box::new(Expression::NumberLiteral(*loc, Type::Uint(64), elem_size)),
+                        )),
+                    )),
+                );
+            }
+        }
+
+        array_offset(
+            loc,
+            array,
+            try_cast(
+                &index_loc,
+                Expression::Variable(index_loc, coerced_ty, pos),
+                &Type::Uint(256),
+                false,
+                ns,
+            )
+            .unwrap(),
+            elem_ty,
+            ns,
+        )
+    } else {
+        match array_ty.deref() {
+            Type::Bytes(array_length) => {
+                let res_ty = Type::Bytes(1);
+                let from_ty = Type::Bytes(*array_length);
+
+                Expression::Trunc(
+                    *loc,
+                    res_ty,
+                    Box::new(Expression::ShiftRight(
+                        *loc,
+                        from_ty.clone(),
+                        Box::new(array),
+                        // shift by (array_length - 1 - index) * 8
+                        Box::new(Expression::ShiftLeft(
+                            *loc,
+                            from_ty.clone(),
+                            Box::new(Expression::Subtract(
+                                *loc,
+                                from_ty.clone(),
+                                Box::new(Expression::NumberLiteral(
+                                    *loc,
+                                    from_ty.clone(),
+                                    BigInt::from_u8(array_length - 1).unwrap(),
+                                )),
+                                Box::new(cast_shift_arg(
+                                    loc,
+                                    Expression::Variable(index_loc, coerced_ty, pos),
+                                    index_width,
+                                    &array_ty,
+                                    ns,
+                                )),
+                            )),
+                            Box::new(Expression::NumberLiteral(
+                                *loc,
+                                from_ty,
+                                BigInt::from_u8(3).unwrap(),
+                            )),
+                        )),
+                        false,
+                    )),
+                )
+            }
+            Type::Array(_, dim) if dim.last().unwrap().is_some() => Expression::ArraySubscript(
+                *loc,
+                array_ty.array_deref(),
+                Box::new(array),
+                Box::new(Expression::Variable(index_loc, coerced_ty, pos)),
+            ),
+            Type::DynamicBytes | Type::Array(_, _) => Expression::DynamicArraySubscript(
+                *loc,
+                array_ty.array_deref(),
+                Box::new(array),
+                Box::new(Expression::Variable(index_loc, coerced_ty, pos)),
+            ),
+            _ => {
+                // should not happen as type-checking already done
+                unreachable!();
+            }
+        }
+    }
+}
+
+fn string_location(
+    loc: &StringLocation,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> StringLocation {
+    match loc {
+        StringLocation::RunTime(s) => {
+            StringLocation::RunTime(Box::new(expression(s, cfg, contract_no, ns, vartab)))
+        }
+        _ => loc.clone(),
+    }
+}

+ 50 - 0
src/codegen/mod.rs

@@ -0,0 +1,50 @@
+pub mod cfg;
+mod expression;
+mod statements;
+mod storage;
+
+use self::cfg::{ControlFlowGraph, Instr, Vartable};
+use self::expression::expression;
+use sema::ast::Namespace;
+
+/// The contracts are fully resolved but they do not have any a CFG which is needed for the llvm code emitter
+/// not all contracts need a cfg; only those for which we need the
+pub fn codegen(contract_no: usize, ns: &mut Namespace) {
+    for function_no in 0..ns.contracts[contract_no].functions.len() {
+        let c = cfg::generate_cfg(contract_no, function_no, ns);
+        ns.contracts[contract_no].functions[function_no].cfg = Some(c);
+    }
+
+    // Generate cfg for storage initializers
+    ns.contracts[contract_no].initializer = storage_initializer(contract_no, ns);
+}
+
+/// This function will set all contract storage initializers and should be called from the constructor
+fn storage_initializer(contract_no: usize, ns: &Namespace) -> ControlFlowGraph {
+    let mut cfg = ControlFlowGraph::new();
+    let mut vartab = Vartable::new();
+
+    for var in &ns.contracts[contract_no].variables {
+        if var.is_storage() {
+            if let Some(init) = &var.initializer {
+                let pos = vartab.temp_name(&var.name, &var.ty);
+                let expr = expression(&init, &mut cfg, contract_no, ns, &mut vartab);
+                cfg.add(&mut vartab, Instr::Set { res: pos, expr });
+                cfg.add(
+                    &mut vartab,
+                    Instr::SetStorage {
+                        local: pos,
+                        ty: var.ty.clone(),
+                        storage: var.get_storage_slot(),
+                    },
+                );
+            }
+        }
+    }
+
+    cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
+
+    cfg.vars = vartab.drain();
+
+    cfg
+}

+ 786 - 0
src/codegen/statements.rs

@@ -0,0 +1,786 @@
+use num_bigint::BigInt;
+use std::collections::LinkedList;
+
+use super::cfg::{ControlFlowGraph, Instr, Vartable};
+use super::expression::{assign_single, emit_function_call, expression};
+use parser::pt;
+use sema::ast::{DestructureField, Expression, Function, Namespace, Parameter, Statement, Type};
+use sema::expression::try_cast;
+
+/// Resolve a statement, which might be a block of statements or an entire body of a function
+pub fn statement(
+    stmt: &Statement,
+    f: &Function,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    loops: &mut LoopScopes,
+) {
+    match stmt {
+        Statement::VariableDecl(_, pos, _, Some(init)) => {
+            let expr = expression(init, cfg, contract_no, ns, vartab);
+
+            cfg.add(vartab, Instr::Set { res: *pos, expr });
+        }
+        Statement::VariableDecl(_, _, _, None) => {
+            // nothing to do
+        }
+        Statement::Return(_, values) => {
+            let values = values
+                .iter()
+                .map(|expr| expression(expr, cfg, contract_no, ns, vartab))
+                .collect();
+
+            cfg.add(vartab, Instr::Return { value: values });
+        }
+        Statement::Expression(_, reachable, expr) => {
+            let expr = expression(expr, cfg, contract_no, ns, vartab);
+
+            if expr != Expression::Poison {
+                cfg.add(vartab, Instr::Eval { expr });
+            }
+
+            if !reachable {
+                cfg.add(vartab, Instr::Unreachable);
+            }
+        }
+        Statement::Delete(_, ty, expr) => {
+            let var_expr = expression(expr, cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::ClearStorage {
+                    ty: ty.clone(),
+                    storage: var_expr,
+                },
+            );
+        }
+        Statement::Break(_) => {
+            cfg.add(
+                vartab,
+                Instr::Branch {
+                    bb: loops.do_break(),
+                },
+            );
+        }
+        Statement::Continue(_) => {
+            cfg.add(
+                vartab,
+                Instr::Branch {
+                    bb: loops.do_continue(),
+                },
+            );
+        }
+        Statement::If(_, _, cond, then_stmt, else_stmt) if else_stmt.is_empty() => {
+            if_then(cond, then_stmt, f, cfg, contract_no, ns, vartab, loops);
+        }
+        Statement::If(_, _, cond, then_stmt, else_stmt) => if_then_else(
+            cond,
+            then_stmt,
+            else_stmt,
+            f,
+            cfg,
+            contract_no,
+            ns,
+            vartab,
+            loops,
+        ),
+        Statement::DoWhile(_, _, body_stmt, cond_expr) => {
+            let body = cfg.new_basic_block("body".to_string());
+            let cond = cfg.new_basic_block("conf".to_string());
+            let end = cfg.new_basic_block("enddowhile".to_string());
+
+            cfg.add(vartab, Instr::Branch { bb: body });
+
+            cfg.set_basic_block(body);
+
+            vartab.new_dirty_tracker();
+            loops.new_scope(end, cond);
+
+            let mut body_reachable = true;
+
+            for stmt in body_stmt {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+                body_reachable = stmt.reachable();
+            }
+
+            if body_reachable {
+                cfg.add(vartab, Instr::Branch { bb: cond });
+            }
+
+            cfg.set_basic_block(cond);
+
+            let cond_expr = expression(cond_expr, cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: cond_expr,
+                    true_: body,
+                    false_: end,
+                },
+            );
+
+            let set = vartab.pop_dirty_tracker();
+            cfg.set_phis(end, set.clone());
+            cfg.set_phis(body, set.clone());
+            cfg.set_phis(cond, set);
+
+            cfg.set_basic_block(end);
+        }
+        Statement::While(_, _, cond_expr, body_stmt) => {
+            let cond = cfg.new_basic_block("cond".to_string());
+            let body = cfg.new_basic_block("body".to_string());
+            let end = cfg.new_basic_block("endwhile".to_string());
+
+            cfg.add(vartab, Instr::Branch { bb: cond });
+
+            cfg.set_basic_block(cond);
+
+            let cond_expr = expression(cond_expr, cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: cond_expr,
+                    true_: body,
+                    false_: end,
+                },
+            );
+
+            cfg.set_basic_block(body);
+
+            vartab.new_dirty_tracker();
+            loops.new_scope(end, cond);
+
+            let mut body_reachable = true;
+
+            for stmt in body_stmt {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+                body_reachable = stmt.reachable();
+            }
+
+            if body_reachable {
+                cfg.add(vartab, Instr::Branch { bb: cond });
+            }
+
+            loops.leave_scope();
+            let set = vartab.pop_dirty_tracker();
+            cfg.set_phis(end, set.clone());
+            cfg.set_phis(cond, set);
+
+            cfg.set_basic_block(end);
+        }
+        Statement::For {
+            init,
+            cond: None,
+            next,
+            body,
+            ..
+        } => {
+            let body_block = cfg.new_basic_block("body".to_string());
+            let next_block = cfg.new_basic_block("next".to_string());
+            let end_block = cfg.new_basic_block("endfor".to_string());
+
+            for stmt in init {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+            }
+
+            cfg.add(vartab, Instr::Branch { bb: body_block });
+
+            cfg.set_basic_block(body_block);
+
+            loops.new_scope(
+                end_block,
+                if next.is_empty() {
+                    body_block
+                } else {
+                    next_block
+                },
+            );
+
+            vartab.new_dirty_tracker();
+
+            let mut body_reachable = true;
+
+            for stmt in body {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+                body_reachable = stmt.reachable();
+            }
+
+            if body_reachable {
+                cfg.add(vartab, Instr::Branch { bb: next_block });
+            }
+
+            loops.leave_scope();
+
+            if body_reachable {
+                if !next.is_empty() {
+                    cfg.set_basic_block(next_block);
+
+                    for stmt in next {
+                        statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+                        body_reachable = stmt.reachable();
+                    }
+                }
+
+                if body_reachable {
+                    cfg.add(vartab, Instr::Branch { bb: body_block });
+                }
+            }
+
+            let set = vartab.pop_dirty_tracker();
+
+            cfg.set_phis(next_block, set.clone());
+            cfg.set_phis(body_block, set.clone());
+            cfg.set_phis(end_block, set);
+
+            cfg.set_basic_block(end_block);
+        }
+        Statement::For {
+            init,
+            cond: Some(cond_expr),
+            next,
+            body,
+            ..
+        } => {
+            let body_block = cfg.new_basic_block("body".to_string());
+            let cond_block = cfg.new_basic_block("cond".to_string());
+            let next_block = cfg.new_basic_block("next".to_string());
+            let end_block = cfg.new_basic_block("endfor".to_string());
+
+            for stmt in init {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+            }
+
+            cfg.add(vartab, Instr::Branch { bb: cond_block });
+
+            cfg.set_basic_block(cond_block);
+
+            let cond_expr = expression(cond_expr, cfg, contract_no, ns, vartab);
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: cond_expr,
+                    true_: body_block,
+                    false_: end_block,
+                },
+            );
+
+            cfg.set_basic_block(body_block);
+
+            // continue goes to next
+            loops.new_scope(end_block, next_block);
+
+            vartab.new_dirty_tracker();
+
+            let mut body_reachable = true;
+
+            for stmt in body {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+                body_reachable = stmt.reachable();
+            }
+
+            if body_reachable {
+                cfg.add(vartab, Instr::Branch { bb: next_block });
+            }
+
+            loops.leave_scope();
+
+            cfg.set_basic_block(next_block);
+
+            let mut next_reachable = true;
+
+            for stmt in next {
+                statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+                next_reachable = stmt.reachable();
+            }
+
+            if next_reachable {
+                cfg.add(vartab, Instr::Branch { bb: cond_block });
+            }
+
+            cfg.set_basic_block(end_block);
+
+            let set = vartab.pop_dirty_tracker();
+            cfg.set_phis(next_block, set.clone());
+            cfg.set_phis(end_block, set.clone());
+            cfg.set_phis(cond_block, set);
+        }
+        Statement::Destructure(_, fields, expr) => {
+            let mut values = match expr {
+                Expression::List(_, exprs) => {
+                    let mut values = Vec::new();
+
+                    for expr in exprs {
+                        let loc = expr.loc();
+                        let expr = expression(expr, cfg, contract_no, ns, vartab);
+                        let ty = expr.ty();
+
+                        let res = vartab.temp_anonymous(&ty);
+
+                        cfg.add(vartab, Instr::Set { res, expr });
+
+                        values.push(Expression::Variable(loc, ty, res));
+                    }
+
+                    values
+                }
+                _ => {
+                    // must be function call, either internal or external
+                    emit_function_call(expr, contract_no, cfg, ns, vartab)
+                }
+            };
+
+            for field in fields.iter() {
+                let right = values.remove(0);
+
+                match field {
+                    DestructureField::None => {
+                        // nothing to do
+                    }
+                    DestructureField::VariableDecl(res, param) => {
+                        // the resolver did not cast the expression
+                        let expr = try_cast(&param.loc, right, &param.ty, true, ns)
+                            .expect("sema should have checked cast");
+
+                        cfg.add(vartab, Instr::Set { res: *res, expr });
+                    }
+                    DestructureField::Expression(left) => {
+                        // the resolver did not cast the expression
+                        let loc = left.loc();
+
+                        let expr = try_cast(&loc, right, left.ty().deref_any(), true, ns)
+                            .expect("sema should have checked cast");
+
+                        assign_single(left, &expr, cfg, contract_no, ns, vartab);
+                    }
+                }
+            }
+        }
+        Statement::TryCatch {
+            expr,
+            returns,
+            ok_stmt,
+            error,
+            catch_param_pos,
+            catch_stmt,
+            ..
+        } => try_catch(
+            expr,
+            returns,
+            ok_stmt,
+            error,
+            catch_param_pos,
+            catch_stmt,
+            f,
+            cfg,
+            contract_no,
+            ns,
+            vartab,
+            loops,
+        ),
+    }
+}
+
+/// Generate if-then-no-else
+fn if_then(
+    cond: &Expression,
+    then_stmt: &[Statement],
+    f: &Function,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    loops: &mut LoopScopes,
+) {
+    let cond = expression(cond, cfg, contract_no, ns, vartab);
+
+    let then = cfg.new_basic_block("then".to_string());
+    let endif = cfg.new_basic_block("endif".to_string());
+
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond,
+            true_: then,
+            false_: endif,
+        },
+    );
+
+    cfg.set_basic_block(then);
+
+    vartab.new_dirty_tracker();
+
+    let mut reachable = true;
+
+    for stmt in then_stmt {
+        statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+        reachable = stmt.reachable();
+    }
+
+    if reachable {
+        cfg.add(vartab, Instr::Branch { bb: endif });
+    }
+
+    cfg.set_phis(endif, vartab.pop_dirty_tracker());
+
+    cfg.set_basic_block(endif);
+}
+
+/// Generate if-then-else
+fn if_then_else(
+    cond: &Expression,
+    then_stmt: &[Statement],
+    else_stmt: &[Statement],
+    f: &Function,
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    loops: &mut LoopScopes,
+) {
+    let cond = expression(cond, cfg, contract_no, ns, vartab);
+
+    let then = cfg.new_basic_block("then".to_string());
+    let else_ = cfg.new_basic_block("else".to_string());
+    let endif = cfg.new_basic_block("endif".to_string());
+
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond,
+            true_: then,
+            false_: else_,
+        },
+    );
+
+    // then
+    cfg.set_basic_block(then);
+
+    vartab.new_dirty_tracker();
+
+    let mut then_reachable = true;
+
+    for stmt in then_stmt {
+        statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+        then_reachable = stmt.reachable();
+    }
+
+    if then_reachable {
+        cfg.add(vartab, Instr::Branch { bb: endif });
+    }
+
+    // else
+    cfg.set_basic_block(else_);
+
+    let mut else_reachable = true;
+
+    for stmt in else_stmt {
+        statement(stmt, f, cfg, contract_no, ns, vartab, loops);
+
+        else_reachable = stmt.reachable();
+    }
+
+    if else_reachable {
+        cfg.add(vartab, Instr::Branch { bb: endif });
+    }
+
+    cfg.set_phis(endif, vartab.pop_dirty_tracker());
+
+    cfg.set_basic_block(endif);
+}
+
+/// Resolve try catch statement
+#[allow(clippy::too_many_arguments)]
+fn try_catch(
+    fcall: &Expression,
+    returns: &[(Option<usize>, Parameter)],
+    ok_stmt: &[Statement],
+    error: &Option<(Option<usize>, Parameter, Vec<Statement>)>,
+    catch_param_pos: &Option<usize>,
+    catch_stmt: &[Statement],
+    f: &Function,
+    cfg: &mut ControlFlowGraph,
+    callee_contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    loops: &mut LoopScopes,
+) {
+    let success = vartab.temp(
+        &pt::Identifier {
+            loc: fcall.loc(),
+            name: "success".to_owned(),
+        },
+        &Type::Bool,
+    );
+
+    let success_block = cfg.new_basic_block("success".to_string());
+    let catch_block = cfg.new_basic_block("catch".to_string());
+    let finally_block = cfg.new_basic_block("finally".to_string());
+
+    match &fcall {
+        Expression::ExternalFunctionCall {
+            contract_no,
+            function_no,
+            address,
+            args,
+            value,
+            gas,
+            ..
+        } => {
+            let address = expression(address, cfg, callee_contract_no, ns, vartab);
+            let value = expression(value, cfg, callee_contract_no, ns, vartab);
+            let gas = expression(gas, cfg, callee_contract_no, ns, vartab);
+
+            let args = args
+                .iter()
+                .map(|a| expression(a, cfg, callee_contract_no, ns, vartab))
+                .collect();
+
+            cfg.add(
+                vartab,
+                Instr::ExternalCall {
+                    success: Some(success),
+                    address,
+                    contract_no: Some(*contract_no),
+                    function_no: *function_no,
+                    args,
+                    value,
+                    gas,
+                },
+            );
+
+            let ftype = &ns.contracts[*contract_no].functions[*function_no];
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: Expression::Variable(fcall.loc(), Type::Bool, success),
+                    true_: success_block,
+                    false_: catch_block,
+                },
+            );
+
+            cfg.set_basic_block(success_block);
+
+            if !ftype.returns.is_empty() {
+                let mut res = Vec::new();
+
+                for ret in returns {
+                    res.push(match ret {
+                        (Some(pos), _) => *pos,
+                        (None, param) => vartab.temp_anonymous(&param.ty),
+                    });
+                }
+
+                cfg.add(
+                    vartab,
+                    Instr::AbiDecode {
+                        res,
+                        selector: None,
+                        exception: None,
+                        tys: ftype.returns.clone(),
+                        data: Expression::ReturnData(pt::Loc(0, 0)),
+                    },
+                );
+            }
+        }
+        Expression::Constructor {
+            contract_no,
+            constructor_no,
+            args,
+            value,
+            gas,
+            salt,
+            ..
+        } => {
+            let address_res = match returns.get(0) {
+                Some((Some(pos), _)) => *pos,
+                _ => vartab.temp_anonymous(&Type::Contract(*contract_no)),
+            };
+
+            let value = match value {
+                Some(v) => Some(expression(v, cfg, callee_contract_no, ns, vartab)),
+                None => None,
+            };
+
+            let gas = expression(gas, cfg, callee_contract_no, ns, vartab);
+            let salt = salt
+                .as_ref()
+                .map(|gas| expression(gas, cfg, callee_contract_no, ns, vartab));
+
+            let args = args
+                .iter()
+                .map(|a| expression(a, cfg, callee_contract_no, ns, vartab))
+                .collect();
+
+            cfg.add(
+                vartab,
+                Instr::Constructor {
+                    success: Some(success),
+                    res: address_res,
+                    contract_no: *contract_no,
+                    constructor_no: *constructor_no,
+                    args,
+                    value,
+                    gas,
+                    salt,
+                },
+            );
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond: Expression::Variable(fcall.loc(), Type::Bool, success),
+                    true_: success_block,
+                    false_: catch_block,
+                },
+            );
+
+            cfg.set_basic_block(success_block);
+        }
+        _ => unreachable!(),
+    }
+
+    vartab.new_dirty_tracker();
+
+    let mut finally_reachable = true;
+
+    for stmt in ok_stmt {
+        statement(stmt, f, cfg, callee_contract_no, ns, vartab, loops);
+
+        finally_reachable = stmt.reachable();
+    }
+
+    if finally_reachable {
+        cfg.add(vartab, Instr::Branch { bb: finally_block });
+    }
+
+    cfg.set_basic_block(catch_block);
+
+    if let Some((error_param_pos, error_param, error_stmt)) = error {
+        let no_reason_block = cfg.new_basic_block("no_reason".to_string());
+
+        let error_var = match error_param_pos {
+            Some(pos) => *pos,
+            _ => vartab.temp_anonymous(&Type::String),
+        };
+
+        cfg.add(
+            vartab,
+            Instr::AbiDecode {
+                selector: Some(0x08c3_79a0),
+                exception: Some(no_reason_block),
+                res: vec![error_var],
+                tys: vec![error_param.clone()],
+                data: Expression::ReturnData(pt::Loc(0, 0)),
+            },
+        );
+
+        let mut reachable = true;
+
+        for stmt in error_stmt {
+            statement(stmt, f, cfg, callee_contract_no, ns, vartab, loops);
+
+            reachable = stmt.reachable();
+        }
+        if reachable {
+            cfg.add(vartab, Instr::Branch { bb: finally_block });
+        }
+
+        cfg.set_basic_block(no_reason_block);
+    }
+
+    if let Some(pos) = catch_param_pos {
+        cfg.add(
+            vartab,
+            Instr::Set {
+                res: *pos,
+                expr: Expression::ReturnData(pt::Loc(0, 0)),
+            },
+        );
+    }
+
+    let mut reachable = true;
+
+    for stmt in catch_stmt {
+        statement(stmt, f, cfg, callee_contract_no, ns, vartab, loops);
+
+        reachable = stmt.reachable();
+    }
+
+    if reachable {
+        cfg.add(vartab, Instr::Branch { bb: finally_block });
+    }
+
+    let set = vartab.pop_dirty_tracker();
+    cfg.set_phis(finally_block, set);
+
+    cfg.set_basic_block(finally_block);
+}
+
+pub struct LoopScope {
+    break_bb: usize,
+    continue_bb: usize,
+}
+
+pub struct LoopScopes(LinkedList<LoopScope>);
+
+impl LoopScopes {
+    pub fn new() -> Self {
+        LoopScopes(LinkedList::new())
+    }
+
+    fn new_scope(&mut self, break_bb: usize, continue_bb: usize) {
+        self.0.push_front(LoopScope {
+            break_bb,
+            continue_bb,
+        })
+    }
+
+    fn leave_scope(&mut self) -> LoopScope {
+        self.0.pop_front().expect("should be in loop scope")
+    }
+
+    fn do_break(&mut self) -> usize {
+        self.0.front().unwrap().break_bb
+    }
+
+    fn do_continue(&mut self) -> usize {
+        self.0.front().unwrap().continue_bb
+    }
+}
+
+impl Type {
+    pub fn default(&self, ns: &Namespace) -> Expression {
+        match self {
+            Type::Address(_) | Type::Uint(_) | Type::Int(_) => {
+                Expression::NumberLiteral(pt::Loc(0, 0), self.clone(), BigInt::from(0))
+            }
+            Type::Bool => Expression::BoolLiteral(pt::Loc(0, 0), false),
+            Type::Bytes(n) => {
+                let mut l = Vec::new();
+                l.resize(*n as usize, 0);
+                Expression::BytesLiteral(pt::Loc(0, 0), self.clone(), l)
+            }
+            Type::Enum(e) => ns.enums[*e].ty.default(ns),
+            Type::Struct(_) => Expression::StructLiteral(pt::Loc(0, 0), self.clone(), Vec::new()),
+            Type::Ref(_) => unreachable!(),
+            Type::StorageRef(_) => Expression::Poison,
+            Type::String | Type::DynamicBytes => {
+                Expression::BytesLiteral(pt::Loc(0, 0), self.clone(), vec![])
+            }
+            _ => unreachable!(),
+        }
+    }
+}

+ 313 - 0
src/codegen/storage.rs

@@ -0,0 +1,313 @@
+use num_bigint::BigInt;
+use num_traits::FromPrimitive;
+use num_traits::One;
+use num_traits::Zero;
+
+use super::cfg::{ControlFlowGraph, Instr, Vartable};
+use super::expression::expression;
+use parser::pt;
+use sema::ast::{Expression, Namespace, Type};
+
+/// Given a storage slot which is the start of the array, calculate the
+/// offset of the array element. This function exists to avoid doing
+/// 256 bit multiply if possible.
+pub fn array_offset(
+    loc: &pt::Loc,
+    start: Expression,
+    index: Expression,
+    elem_ty: Type,
+    ns: &Namespace,
+) -> Expression {
+    let elem_size = elem_ty.storage_slots(ns);
+    let slot_ty = Type::Uint(256);
+
+    // the index needs to be cast to i256 and multiplied by the number
+    // of slots for each element
+    if elem_size == BigInt::one() {
+        Expression::Add(*loc, slot_ty, Box::new(start), Box::new(index))
+    } else if (elem_size.clone() & (elem_size.clone() - BigInt::one())) == BigInt::zero() {
+        // elem_size is power of 2
+        Expression::ShiftLeft(
+            *loc,
+            slot_ty.clone(),
+            Box::new(start),
+            Box::new(Expression::ShiftLeft(
+                *loc,
+                slot_ty.clone(),
+                Box::new(index),
+                Box::new(Expression::NumberLiteral(
+                    *loc,
+                    slot_ty,
+                    BigInt::from_usize(elem_size.bits()).unwrap(),
+                )),
+            )),
+        )
+    } else {
+        Expression::Add(
+            *loc,
+            slot_ty.clone(),
+            Box::new(start),
+            Box::new(Expression::Multiply(
+                *loc,
+                slot_ty.clone(),
+                Box::new(index),
+                Box::new(Expression::NumberLiteral(*loc, slot_ty, elem_size)),
+            )),
+        )
+    }
+}
+
+/// Push() method on dynamic array in storage
+pub fn array_push(
+    loc: &pt::Loc,
+    args: &[Expression],
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    // set array+length to val_expr
+    let slot_ty = Type::Uint(256);
+    let length_pos = vartab.temp_anonymous(&slot_ty);
+
+    let var_expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: length_pos,
+            expr: Expression::StorageLoad(*loc, slot_ty.clone(), Box::new(var_expr.clone())),
+        },
+    );
+
+    let elem_ty = args[0].ty().storage_array_elem();
+
+    let entry_pos = vartab.temp_anonymous(&slot_ty);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: entry_pos,
+            expr: array_offset(
+                loc,
+                Expression::Keccak256(*loc, slot_ty.clone(), vec![var_expr.clone()]),
+                Expression::Variable(*loc, slot_ty.clone(), length_pos),
+                elem_ty.clone(),
+                ns,
+            ),
+        },
+    );
+
+    if args.len() == 2 {
+        let expr = expression(&args[1], cfg, contract_no, ns, vartab);
+
+        let pos = vartab.temp_anonymous(&elem_ty);
+
+        cfg.add(vartab, Instr::Set { res: pos, expr });
+
+        cfg.add(
+            vartab,
+            Instr::SetStorage {
+                ty: elem_ty.clone(),
+                local: pos,
+                storage: Expression::Variable(*loc, slot_ty.clone(), entry_pos),
+            },
+        );
+    }
+
+    // increase length
+    let new_length = vartab.temp_anonymous(&slot_ty);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: new_length,
+            expr: Expression::Add(
+                *loc,
+                slot_ty.clone(),
+                Box::new(Expression::Variable(*loc, slot_ty.clone(), length_pos)),
+                Box::new(Expression::NumberLiteral(
+                    *loc,
+                    slot_ty.clone(),
+                    BigInt::one(),
+                )),
+            ),
+        },
+    );
+
+    cfg.add(
+        vartab,
+        Instr::SetStorage {
+            ty: slot_ty,
+            local: new_length,
+            storage: var_expr,
+        },
+    );
+
+    if args.len() == 1 {
+        Expression::Variable(*loc, elem_ty, entry_pos)
+    } else {
+        Expression::Poison
+    }
+}
+
+/// Pop() method on dynamic array in storage
+pub fn array_pop(
+    loc: &pt::Loc,
+    args: &[Expression],
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    // set array+length to val_expr
+    let slot_ty = Type::Uint(256);
+    let length_ty = Type::Uint(256);
+    let length_pos = vartab.temp_anonymous(&slot_ty);
+
+    let ty = args[0].ty();
+    let var_expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: length_pos,
+            expr: Expression::StorageLoad(*loc, length_ty.clone(), Box::new(var_expr.clone())),
+        },
+    );
+
+    let empty_array = cfg.new_basic_block("empty_array".to_string());
+    let has_elements = cfg.new_basic_block("has_elements".to_string());
+
+    cfg.writes_contract_storage = true;
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: Expression::Equal(
+                *loc,
+                Box::new(Expression::Variable(*loc, length_ty.clone(), length_pos)),
+                Box::new(Expression::NumberLiteral(
+                    *loc,
+                    length_ty.clone(),
+                    BigInt::zero(),
+                )),
+            ),
+            true_: empty_array,
+            false_: has_elements,
+        },
+    );
+
+    cfg.set_basic_block(empty_array);
+    cfg.add(vartab, Instr::AssertFailure { expr: None });
+
+    cfg.set_basic_block(has_elements);
+    let new_length = vartab.temp_anonymous(&slot_ty);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: new_length,
+            expr: Expression::Subtract(
+                *loc,
+                length_ty.clone(),
+                Box::new(Expression::Variable(*loc, length_ty.clone(), length_pos)),
+                Box::new(Expression::NumberLiteral(*loc, length_ty, BigInt::one())),
+            ),
+        },
+    );
+
+    // The array element will be loaded before clearing. So, the return
+    // type of pop() is the derefenced array dereference
+    let elem_ty = ty.storage_array_elem().deref_any().clone();
+    let entry_pos = vartab.temp_anonymous(&slot_ty);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: entry_pos,
+            expr: array_offset(
+                loc,
+                Expression::Keccak256(*loc, slot_ty.clone(), vec![var_expr.clone()]),
+                Expression::Variable(*loc, slot_ty.clone(), new_length),
+                elem_ty.clone(),
+                ns,
+            ),
+        },
+    );
+
+    let res_pos = vartab.temp_anonymous(&elem_ty);
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            res: res_pos,
+            expr: Expression::StorageLoad(
+                *loc,
+                elem_ty.clone(),
+                Box::new(Expression::Variable(*loc, elem_ty.clone(), entry_pos)),
+            ),
+        },
+    );
+
+    cfg.add(
+        vartab,
+        Instr::ClearStorage {
+            ty: elem_ty.clone(),
+            storage: Expression::Variable(*loc, slot_ty.clone(), entry_pos),
+        },
+    );
+
+    // set decrease length
+    cfg.add(
+        vartab,
+        Instr::SetStorage {
+            ty: slot_ty,
+            local: new_length,
+            storage: var_expr,
+        },
+    );
+
+    Expression::Variable(*loc, elem_ty, res_pos)
+}
+
+/// Push() method on dynamic bytes in storage
+pub fn bytes_push(
+    loc: &pt::Loc,
+    args: &[Expression],
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    let var_expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+    if args.len() > 1 {
+        let val = expression(&args[1], cfg, contract_no, ns, vartab);
+
+        Expression::StorageBytesPush(*loc, Box::new(var_expr), Box::new(val))
+    } else {
+        Expression::StorageBytesPush(
+            *loc,
+            Box::new(var_expr),
+            Box::new(Expression::NumberLiteral(
+                *loc,
+                Type::Bytes(1),
+                BigInt::zero(),
+            )),
+        )
+    }
+}
+
+/// Pop() method on dynamic bytes in storage
+pub fn bytes_pop(
+    loc: &pt::Loc,
+    args: &[Expression],
+    cfg: &mut ControlFlowGraph,
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+) -> Expression {
+    let var_expr = expression(&args[0], cfg, contract_no, ns, vartab);
+
+    Expression::StorageBytesPop(*loc, Box::new(var_expr))
+}

+ 82 - 94
src/emit/ethabiencoder.rs

@@ -1,10 +1,9 @@
-use num_traits::ToPrimitive;
-use resolver;
-
 use inkwell::types::BasicType;
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
 use inkwell::IntPredicate;
+use num_traits::ToPrimitive;
+use sema::ast;
 
 use super::Contract;
 
@@ -18,19 +17,19 @@ impl EthAbiEncoder {
         contract: &Contract<'a>,
         load: bool,
         function: FunctionValue,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         arg: BasicValueEnum<'a>,
         fixed: &mut PointerValue<'a>,
         offset: &mut IntValue<'a>,
         dynamic: &mut PointerValue<'a>,
     ) {
         match &ty {
-            resolver::Type::Bool
-            | resolver::Type::Address(_)
-            | resolver::Type::Contract(_)
-            | resolver::Type::Int(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Bytes(_) => {
+            ast::Type::Bool
+            | ast::Type::Address(_)
+            | ast::Type::Contract(_)
+            | ast::Type::Int(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Bytes(_) => {
                 self.encode_primitive(contract, load, ty, *fixed, arg);
 
                 *fixed = unsafe {
@@ -41,10 +40,10 @@ impl EthAbiEncoder {
                     )
                 };
             }
-            resolver::Type::Enum(n) => {
+            ast::Type::Enum(n) => {
                 self.encode_primitive(contract, load, &contract.ns.enums[*n].ty, *fixed, arg);
             }
-            resolver::Type::Array(_, dim) => {
+            ast::Type::Array(_, dim) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -75,7 +74,7 @@ impl EthAbiEncoder {
                                 contract,
                                 true,
                                 function,
-                                &ty.deref(),
+                                &ty.deref_any(),
                                 elem.into(),
                                 data,
                                 offset,
@@ -88,7 +87,7 @@ impl EthAbiEncoder {
                     self.encode_primitive(
                         contract,
                         false,
-                        &resolver::Type::Uint(32),
+                        &ast::Type::Uint(32),
                         *fixed,
                         (*offset).into(),
                     );
@@ -122,7 +121,7 @@ impl EthAbiEncoder {
                     self.encode_primitive(
                         contract,
                         false,
-                        &resolver::Type::Uint(32),
+                        &ast::Type::Uint(32),
                         *dynamic,
                         len.into(),
                     );
@@ -204,7 +203,7 @@ impl EthAbiEncoder {
                                 contract,
                                 true,
                                 function,
-                                &ty.deref(),
+                                &ty.deref_any(),
                                 elem.into(),
                                 data,
                                 offset,
@@ -214,7 +213,7 @@ impl EthAbiEncoder {
                     );
                 }
             }
-            resolver::Type::Struct(n) => {
+            ast::Type::Struct(n) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -245,18 +244,15 @@ impl EthAbiEncoder {
                     );
                 }
             }
-            resolver::Type::Undef => unreachable!(),
-            resolver::Type::StorageRef(_) => unreachable!(),
-            resolver::Type::Mapping(_, _) => unreachable!(),
-            resolver::Type::Ref(ty) => {
+            ast::Type::Ref(ty) => {
                 self.encode_ty(contract, load, function, ty, arg, fixed, offset, dynamic);
             }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::String | ast::Type::DynamicBytes => {
                 // write the current offset to fixed
                 self.encode_primitive(
                     contract,
                     false,
-                    &resolver::Type::Uint(32),
+                    &ast::Type::Uint(32),
                     *fixed,
                     (*offset).into(),
                 );
@@ -293,13 +289,7 @@ impl EthAbiEncoder {
                     .into_int_value();
 
                 // write the current offset to fixed
-                self.encode_primitive(
-                    contract,
-                    false,
-                    &resolver::Type::Uint(32),
-                    *dynamic,
-                    len.into(),
-                );
+                self.encode_primitive(contract, false, &ast::Type::Uint(32), *dynamic, len.into());
 
                 *dynamic = unsafe {
                     contract.builder.build_gep(
@@ -366,6 +356,7 @@ impl EthAbiEncoder {
 
                 *offset = contract.builder.build_int_add(*offset, len, "");
             }
+            _ => unreachable!(),
         };
     }
 
@@ -374,12 +365,12 @@ impl EthAbiEncoder {
         &self,
         contract: &Contract,
         load: bool,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         dest: PointerValue,
         arg: BasicValueEnum,
     ) {
         match ty {
-            resolver::Type::Bool => {
+            ast::Type::Bool => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -409,7 +400,7 @@ impl EthAbiEncoder {
 
                 contract.builder.build_store(dest, value);
             }
-            resolver::Type::Int(8) | resolver::Type::Uint(8) => {
+            ast::Type::Int(8) | ast::Type::Uint(8) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -422,7 +413,7 @@ impl EthAbiEncoder {
                     "destvoid",
                 );
 
-                if let resolver::Type::Int(_) = ty {
+                if let ast::Type::Int(_) = ty {
                     let negative = contract.builder.build_int_compare(
                         IntPredicate::SLT,
                         arg.into_int_value(),
@@ -461,18 +452,18 @@ impl EthAbiEncoder {
 
                 contract.builder.build_store(dest, arg);
             }
-            resolver::Type::Contract(_)
-            | resolver::Type::Address(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Int(_)
+            ast::Type::Contract(_)
+            | ast::Type::Address(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Int(_)
                 if load =>
             {
                 let n = match ty {
-                    resolver::Type::Contract(_) | resolver::Type::Address(_) => {
+                    ast::Type::Contract(_) | ast::Type::Address(_) => {
                         contract.ns.address_length as u16 * 8
                     }
-                    resolver::Type::Uint(b) => *b,
-                    resolver::Type::Int(b) => *b,
+                    ast::Type::Uint(b) => *b,
+                    ast::Type::Int(b) => *b,
                     _ => unreachable!(),
                 };
 
@@ -490,7 +481,7 @@ impl EthAbiEncoder {
 
                 // first clear/set the upper bits
                 if n < 256 {
-                    if let resolver::Type::Int(_) = ty {
+                    if let ast::Type::Int(_) = ty {
                         let signdest = unsafe {
                             contract.builder.build_gep(
                                 arg8,
@@ -548,18 +539,18 @@ impl EthAbiEncoder {
                     "",
                 );
             }
-            resolver::Type::Contract(_)
-            | resolver::Type::Address(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Int(_)
+            ast::Type::Contract(_)
+            | ast::Type::Address(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Int(_)
                 if !load =>
             {
                 let n = match ty {
-                    resolver::Type::Contract(_) | resolver::Type::Address(_) => {
+                    ast::Type::Contract(_) | ast::Type::Address(_) => {
                         contract.ns.address_length as u16 * 8
                     }
-                    resolver::Type::Uint(b) => *b,
-                    resolver::Type::Int(b) => *b,
+                    ast::Type::Uint(b) => *b,
+                    ast::Type::Int(b) => *b,
                     _ => unreachable!(),
                 };
 
@@ -571,7 +562,7 @@ impl EthAbiEncoder {
 
                 // first clear/set the upper bits
                 if n < 256 {
-                    if let resolver::Type::Int(_) = ty {
+                    if let ast::Type::Int(_) = ty {
                         let negative = contract.builder.build_int_compare(
                             IntPredicate::SLT,
                             arg.into_int_value(),
@@ -628,7 +619,7 @@ impl EthAbiEncoder {
                     "",
                 );
             }
-            resolver::Type::Bytes(1) => {
+            ast::Type::Bytes(1) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -643,7 +634,7 @@ impl EthAbiEncoder {
 
                 contract.builder.build_store(dest8, arg);
             }
-            resolver::Type::Bytes(n) => {
+            ast::Type::Bytes(n) => {
                 let val = if load {
                     arg.into_pointer_value()
                 } else {
@@ -693,12 +684,12 @@ impl EthAbiEncoder {
         &self,
         arg: BasicValueEnum<'a>,
         load: bool,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         function: FunctionValue,
         contract: &Contract<'a>,
     ) -> IntValue<'a> {
         match ty {
-            resolver::Type::Struct(n) => {
+            ast::Type::Struct(n) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -732,7 +723,7 @@ impl EthAbiEncoder {
 
                 sum
             }
-            resolver::Type::Array(_, dims) => {
+            ast::Type::Array(_, dims) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -844,7 +835,7 @@ impl EthAbiEncoder {
 
                 sum
             }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::String | ast::Type::DynamicBytes => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -883,23 +874,23 @@ impl EthAbiEncoder {
     }
 
     /// Return the encoded length of the given type, fixed part only
-    pub fn encoded_fixed_length(&self, ty: &resolver::Type, ns: &resolver::Namespace) -> u64 {
+    pub fn encoded_fixed_length(&self, ty: &ast::Type, ns: &ast::Namespace) -> u64 {
         match ty {
-            resolver::Type::Bool
-            | resolver::Type::Contract(_)
-            | resolver::Type::Address(_)
-            | resolver::Type::Int(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Bytes(_) => 32,
+            ast::Type::Bool
+            | ast::Type::Contract(_)
+            | ast::Type::Address(_)
+            | ast::Type::Int(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Bytes(_) => 32,
             // String and Dynamic bytes use 32 bytes for the offset into dynamic encoded
-            resolver::Type::String | resolver::Type::DynamicBytes => 32,
-            resolver::Type::Enum(_) => 32,
-            resolver::Type::Struct(n) => ns.structs[*n]
+            ast::Type::String | ast::Type::DynamicBytes => 32,
+            ast::Type::Enum(_) => 32,
+            ast::Type::Struct(n) => ns.structs[*n]
                 .fields
                 .iter()
                 .map(|f| self.encoded_fixed_length(&f.ty, ns))
                 .sum(),
-            resolver::Type::Array(ty, dims) => {
+            ast::Type::Array(ty, dims) => {
                 let mut product = 1;
 
                 for dim in dims {
@@ -913,10 +904,9 @@ impl EthAbiEncoder {
 
                 product * self.encoded_fixed_length(&ty, ns)
             }
-            resolver::Type::Undef => unreachable!(),
-            resolver::Type::Mapping(_, _) => unreachable!(),
-            resolver::Type::Ref(r) => self.encoded_fixed_length(r, ns),
-            resolver::Type::StorageRef(r) => self.encoded_fixed_length(r, ns),
+            ast::Type::Ref(r) => self.encoded_fixed_length(r, ns),
+            ast::Type::StorageRef(r) => self.encoded_fixed_length(r, ns),
+            _ => unreachable!(),
         }
     }
 
@@ -925,7 +915,7 @@ impl EthAbiEncoder {
         &self,
         contract: &Contract<'b>,
         function: FunctionValue,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         to: Option<PointerValue<'b>>,
         offset: &mut IntValue<'b>,
         data: PointerValue<'b>,
@@ -944,14 +934,14 @@ impl EthAbiEncoder {
 
         *offset = new_offset;
 
-        let ty = if let resolver::Type::Enum(n) = ty {
+        let ty = if let ast::Type::Enum(n) = ty {
             &contract.ns.enums[*n].ty
         } else {
             ty
         };
 
         match &ty {
-            resolver::Type::Bool => {
+            ast::Type::Bool => {
                 // solidity checks all the 32 bytes for being non-zero; we will just look at the upper 8 bytes, else we would need four loads
                 // which is unneeded (hopefully)
                 // cast to 64 bit pointer
@@ -983,7 +973,7 @@ impl EthAbiEncoder {
                 }
                 val.into()
             }
-            resolver::Type::Uint(8) | resolver::Type::Int(8) => {
+            ast::Type::Uint(8) | ast::Type::Int(8) => {
                 let int8_ptr = unsafe {
                     contract.builder.build_gep(
                         data,
@@ -1000,7 +990,7 @@ impl EthAbiEncoder {
 
                 val
             }
-            resolver::Type::Address(_) | resolver::Type::Contract(_) => {
+            ast::Type::Address(_) | ast::Type::Contract(_) => {
                 let int_type = contract
                     .context
                     .custom_width_int_type(contract.ns.address_length as u32 * 8);
@@ -1035,7 +1025,7 @@ impl EthAbiEncoder {
                     store.into()
                 }
             }
-            resolver::Type::Uint(n) | resolver::Type::Int(n) => {
+            ast::Type::Uint(n) | ast::Type::Int(n) => {
                 let int_type = contract.context.custom_width_int_type(*n as u32);
                 let type_size = int_type.size_of();
 
@@ -1067,7 +1057,7 @@ impl EthAbiEncoder {
                     store.into()
                 }
             }
-            resolver::Type::Bytes(1) => {
+            ast::Type::Bytes(1) => {
                 let val = contract.builder.build_load(data, "bytes1");
 
                 if let Some(p) = to {
@@ -1075,7 +1065,7 @@ impl EthAbiEncoder {
                 }
                 val
             }
-            resolver::Type::Bytes(b) => {
+            ast::Type::Bytes(b) => {
                 let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
                 let type_size = int_type.size_of();
 
@@ -1116,15 +1106,15 @@ impl EthAbiEncoder {
         &self,
         contract: &Contract<'b>,
         function: FunctionValue,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         to: Option<PointerValue<'b>>,
         offset: &mut IntValue<'b>,
         data: PointerValue<'b>,
         length: IntValue<'b>,
     ) -> BasicValueEnum<'b> {
         match &ty {
-            resolver::Type::Array(_, dim) => {
-                let llvm_ty = contract.llvm_type(ty.deref());
+            ast::Type::Array(_, dim) => {
+                let llvm_ty = contract.llvm_type(ty.deref_any());
 
                 let size = llvm_ty
                     .size_of()
@@ -1185,7 +1175,7 @@ impl EthAbiEncoder {
                         self.decode_primitive(
                             contract,
                             function,
-                            &resolver::Type::Uint(32),
+                            &ast::Type::Uint(32),
                             None,
                             offset,
                             data,
@@ -1199,7 +1189,7 @@ impl EthAbiEncoder {
                         .decode_primitive(
                             contract,
                             function,
-                            &resolver::Type::Uint(32),
+                            &ast::Type::Uint(32),
                             None,
                             &mut dataoffset,
                             data,
@@ -1207,7 +1197,7 @@ impl EthAbiEncoder {
                         )
                         .into_int_value();
 
-                    let elem_ty = contract.llvm_var(&ty.deref());
+                    let elem_ty = contract.llvm_var(&ty.deref_any());
                     let elem_size = elem_ty
                         .size_of()
                         .unwrap()
@@ -1287,8 +1277,8 @@ impl EthAbiEncoder {
 
                 dest.into()
             }
-            resolver::Type::Struct(n) => {
-                let llvm_ty = contract.llvm_type(ty.deref());
+            ast::Type::Struct(n) => {
+                let llvm_ty = contract.llvm_type(ty.deref_any());
 
                 let size = llvm_ty
                     .size_of()
@@ -1342,17 +1332,15 @@ impl EthAbiEncoder {
 
                 struct_pointer.into()
             }
-            resolver::Type::Ref(ty) => {
-                self.decode_ty(contract, function, ty, to, offset, data, length)
-            }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::Ref(ty) => self.decode_ty(contract, function, ty, to, offset, data, length),
+            ast::Type::String | ast::Type::DynamicBytes => {
                 // we read the offset and the length as 32 bits. Since we are in 32 bits wasm,
                 // we cannot deal with more than 4GB of abi encoded data.
                 let mut dataoffset = contract.builder.build_int_z_extend(
                     self.decode_primitive(
                         contract,
                         function,
-                        &resolver::Type::Uint(32),
+                        &ast::Type::Uint(32),
                         None,
                         offset,
                         data,
@@ -1367,7 +1355,7 @@ impl EthAbiEncoder {
                     self.decode_primitive(
                         contract,
                         function,
-                        &resolver::Type::Uint(32),
+                        &ast::Type::Uint(32),
                         None,
                         &mut dataoffset,
                         data,
@@ -1468,7 +1456,7 @@ impl EthAbiEncoder {
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
         datalength: IntValue<'b>,
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) {
         let data = contract.builder.build_pointer_cast(
             data,

+ 11 - 12
src/emit/ewasm.rs

@@ -1,6 +1,6 @@
+use codegen::cfg::HashTy;
 use parser::pt;
-use resolver;
-use resolver::cfg::HashTy;
+use sema::ast;
 use std::cell::RefCell;
 use std::str;
 
@@ -24,8 +24,8 @@ pub struct EwasmTarget {
 impl EwasmTarget {
     pub fn build<'a>(
         context: &'a Context,
-        contract: &'a resolver::Contract,
-        ns: &'a resolver::Namespace,
+        contract: &'a ast::Contract,
+        ns: &'a ast::Namespace,
         filename: &'a str,
         opt: OptimizationLevel,
     ) -> Contract<'a> {
@@ -79,7 +79,7 @@ impl EwasmTarget {
             "storageLoad",
             "finish",
             "revert",
-            "copyCopy",
+            "codeCopy",
             "getCodeSize",
             "printMem",
             "call",
@@ -587,7 +587,7 @@ impl EwasmTarget {
         load: bool,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
         let mut offset = contract.context.i32_type().const_int(
             spec.iter()
@@ -1070,7 +1070,7 @@ impl TargetRuntime for EwasmTarget {
         load: bool,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
         self.encode(contract, selector, None, load, function, args, spec)
     }
@@ -1082,7 +1082,7 @@ impl TargetRuntime for EwasmTarget {
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
         length: IntValue<'b>,
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) {
         self.abi
             .decode(contract, function, args, data, length, spec);
@@ -1570,10 +1570,9 @@ impl TargetRuntime for EwasmTarget {
         }
 
         // bytes32 needs to reverse bytes
-        let temp = contract.builder.build_alloca(
-            contract.llvm_type(&resolver::Type::Bytes(hashlen as u8)),
-            "hash",
-        );
+        let temp = contract
+            .builder
+            .build_alloca(contract.llvm_type(&ast::Type::Bytes(hashlen as u8)), "hash");
 
         contract.builder.build_call(
             contract.module.get_function("__beNtoleN").unwrap(),

+ 118 - 119
src/emit/mod.rs

@@ -1,8 +1,8 @@
+use codegen::cfg;
 use hex;
 use parser::pt;
-use resolver;
-use resolver::cfg;
-use resolver::expression::{Expression, StringLocation};
+use sema::ast;
+use sema::ast::{Expression, StringLocation};
 use std::cell::RefCell;
 use std::path::Path;
 use std::str;
@@ -54,7 +54,7 @@ pub trait TargetRuntime {
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
         length: IntValue<'b>,
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     );
 
     /// Abi encode with optional four bytes selector. The load parameter should be set if the args are
@@ -66,7 +66,7 @@ pub trait TargetRuntime {
         load: bool,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>);
 
     // Access storage
@@ -225,8 +225,8 @@ pub struct Contract<'a> {
     builder: Builder<'a>,
     context: &'a Context,
     triple: TargetTriple,
-    contract: &'a resolver::Contract,
-    ns: &'a resolver::Namespace,
+    contract: &'a ast::Contract,
+    ns: &'a ast::Namespace,
     functions: Vec<FunctionValue<'a>>,
     wasm: RefCell<Vec<u8>>,
     opt: OptimizationLevel,
@@ -237,8 +237,8 @@ impl<'a> Contract<'a> {
     /// Build the LLVM IR for a contract
     pub fn build(
         context: &'a Context,
-        contract: &'a resolver::Contract,
-        ns: &'a resolver::Namespace,
+        contract: &'a ast::Contract,
+        ns: &'a ast::Namespace,
         filename: &'a str,
         opt: OptimizationLevel,
     ) -> Self {
@@ -359,8 +359,8 @@ impl<'a> Contract<'a> {
 
     pub fn new(
         context: &'a Context,
-        contract: &'a resolver::Contract,
-        ns: &'a resolver::Namespace,
+        contract: &'a ast::Contract,
+        ns: &'a ast::Namespace,
         filename: &'a str,
         opt: OptimizationLevel,
         runtime: Option<Box<Contract<'a>>>,
@@ -678,25 +678,25 @@ impl<'a> Contract<'a> {
     fn emit_functions(&mut self, runtime: &mut dyn TargetRuntime) {
         let mut defines = Vec::new();
 
-        for resolver_func in &self.contract.functions {
-            let name = match resolver_func.ty {
+        for codegen_func in &self.contract.functions {
+            let name = match codegen_func.ty {
                 pt::FunctionTy::Function => {
-                    format!("sol::function::{}", resolver_func.wasm_symbol(self.ns))
+                    format!("sol::function::{}", codegen_func.wasm_symbol(self.ns))
                 }
                 pt::FunctionTy::Constructor => {
-                    format!("sol::constructor{}", resolver_func.wasm_symbol(self.ns))
+                    format!("sol::constructor{}", codegen_func.wasm_symbol(self.ns))
                 }
-                _ => format!("sol::{}", resolver_func.ty),
+                _ => format!("sol::{}", codegen_func.ty),
             };
 
-            let func_decl = self.declare_function(&name, resolver_func);
+            let func_decl = self.declare_function(&name, codegen_func);
             self.functions.push(func_decl);
 
-            defines.push((func_decl, resolver_func));
+            defines.push((func_decl, codegen_func));
         }
 
-        for (func_decl, resolver_func) in defines {
-            self.define_function(resolver_func, func_decl, runtime);
+        for (func_decl, codegen_func) in defines {
+            self.define_function(codegen_func, func_decl, runtime);
         }
     }
 
@@ -713,13 +713,15 @@ impl<'a> Contract<'a> {
         runtime: &dyn TargetRuntime,
     ) -> BasicValueEnum<'a> {
         match e {
-            Expression::FunctionArg(_, pos) => function.get_nth_param(*pos as u32).unwrap(),
+            Expression::FunctionArg(_, _, pos) => function.get_nth_param(*pos as u32).unwrap(),
             Expression::BoolLiteral(_, val) => self
                 .context
                 .bool_type()
                 .const_int(*val as u64, false)
                 .into(),
-            Expression::NumberLiteral(_, bits, n) => self.number_literal(*bits as u32, n).into(),
+            Expression::NumberLiteral(_, ty, n) => {
+                self.number_literal(ty.bits(self.ns) as u32, n).into()
+            }
             Expression::StructLiteral(_, ty, exprs) => {
                 let struct_ty = self.llvm_type(ty);
 
@@ -763,7 +765,7 @@ impl<'a> Contract<'a> {
 
                 s.into()
             }
-            Expression::BytesLiteral(_, bs) => {
+            Expression::BytesLiteral(_, _, bs) => {
                 let ty = self.context.custom_width_int_type((bs.len() * 8) as u32);
 
                 // hex"11223344" should become i32 0x11223344
@@ -774,10 +776,10 @@ impl<'a> Contract<'a> {
                     .into()
             }
             Expression::CodeLiteral(_, contract_no, runtime) => {
-                let resolver_contract = &self.ns.contracts[*contract_no];
+                let codegen_contract = &self.ns.contracts[*contract_no];
 
                 let target_contract =
-                    Contract::build(self.context, &resolver_contract, self.ns, "", self.opt);
+                    Contract::build(self.context, &codegen_contract, self.ns, "", self.opt);
 
                 // wasm
                 let wasm = if *runtime && target_contract.runtime.is_some() {
@@ -798,7 +800,7 @@ impl<'a> Contract<'a> {
                     &format!(
                         "code_{}_{}",
                         if *runtime { "runtime" } else { "deployer" },
-                        &resolver_contract.name
+                        &codegen_contract.name
                     ),
                     &wasm,
                     false,
@@ -826,7 +828,7 @@ impl<'a> Contract<'a> {
                     )
                     .into()
             }
-            Expression::Add(_, l, r) => {
+            Expression::Add(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -836,7 +838,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_int_add(left, right, "").into()
             }
-            Expression::Subtract(_, l, r) => {
+            Expression::Subtract(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -846,7 +848,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_int_sub(left, right, "").into()
             }
-            Expression::Multiply(_, l, r) => {
+            Expression::Multiply(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -901,7 +903,7 @@ impl<'a> Contract<'a> {
                     self.builder.build_int_mul(left, right, "").into()
                 }
             }
-            Expression::UDivide(_, l, r) => {
+            Expression::UDivide(_, _, l, r) => {
                 let left = self.expression(l, vartab, function, runtime);
                 let right = self.expression(r, vartab, function, runtime);
 
@@ -948,7 +950,7 @@ impl<'a> Contract<'a> {
                         .into()
                 }
             }
-            Expression::SDivide(_, l, r) => {
+            Expression::SDivide(_, _, l, r) => {
                 let left = self.expression(l, vartab, function, runtime);
                 let right = self.expression(r, vartab, function, runtime);
 
@@ -991,7 +993,7 @@ impl<'a> Contract<'a> {
                         .into()
                 }
             }
-            Expression::UModulo(_, l, r) => {
+            Expression::UModulo(_, _, l, r) => {
                 let left = self.expression(l, vartab, function, runtime);
                 let right = self.expression(r, vartab, function, runtime);
 
@@ -1038,7 +1040,7 @@ impl<'a> Contract<'a> {
                         .into()
                 }
             }
-            Expression::SModulo(_, l, r) => {
+            Expression::SModulo(_, _, l, r) => {
                 let left = self.expression(l, vartab, function, runtime);
                 let right = self.expression(r, vartab, function, runtime);
 
@@ -1080,7 +1082,7 @@ impl<'a> Contract<'a> {
                         .into()
                 }
             }
-            Expression::Power(_, l, r) => {
+            Expression::Power(_, _, l, r) => {
                 let left = self.expression(l, vartab, function, runtime);
                 let right = self.expression(r, vartab, function, runtime);
 
@@ -1214,8 +1216,8 @@ impl<'a> Contract<'a> {
                     .build_int_compare(IntPredicate::ULE, left, right, "")
                     .into()
             }
-            Expression::Variable(_, s) => vartab[*s].value,
-            Expression::Load(_, e) => {
+            Expression::Variable(_, _, s) => vartab[*s].value,
+            Expression::Load(_, _, e) => {
                 let expr = self
                     .expression(e, vartab, function, runtime)
                     .into_pointer_value();
@@ -1241,7 +1243,7 @@ impl<'a> Contract<'a> {
                     .build_int_z_extend(e, ty.into_int_type(), "")
                     .into()
             }
-            Expression::UnaryMinus(_, e) => {
+            Expression::UnaryMinus(_, _, e) => {
                 let e = self
                     .expression(e, vartab, function, runtime)
                     .into_int_value();
@@ -1268,6 +1270,7 @@ impl<'a> Contract<'a> {
                     .build_int_truncate(e, ty.into_int_type(), "")
                     .into()
             }
+            Expression::Cast(_, _, e) => self.expression(e, vartab, function, runtime),
             Expression::Not(_, e) => {
                 let e = self
                     .expression(e, vartab, function, runtime)
@@ -1277,7 +1280,7 @@ impl<'a> Contract<'a> {
                     .build_int_compare(IntPredicate::EQ, e, e.get_type().const_zero(), "")
                     .into()
             }
-            Expression::Complement(_, e) => {
+            Expression::Complement(_, _, e) => {
                 let e = self
                     .expression(e, vartab, function, runtime)
                     .into_int_value();
@@ -1304,7 +1307,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_and(left, right, "").into()
             }
-            Expression::BitwiseOr(_, l, r) => {
+            Expression::BitwiseOr(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -1314,7 +1317,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_or(left, right, "").into()
             }
-            Expression::BitwiseAnd(_, l, r) => {
+            Expression::BitwiseAnd(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -1324,7 +1327,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_and(left, right, "").into()
             }
-            Expression::BitwiseXor(_, l, r) => {
+            Expression::BitwiseXor(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -1334,7 +1337,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_xor(left, right, "").into()
             }
-            Expression::ShiftLeft(_, l, r) => {
+            Expression::ShiftLeft(_, _, l, r) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -1344,7 +1347,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_left_shift(left, right, "").into()
             }
-            Expression::ShiftRight(_, l, r, signed) => {
+            Expression::ShiftRight(_, _, l, r, signed) => {
                 let left = self
                     .expression(l, vartab, function, runtime)
                     .into_int_value();
@@ -1356,7 +1359,7 @@ impl<'a> Contract<'a> {
                     .build_right_shift(left, right, *signed, "")
                     .into()
             }
-            Expression::ArraySubscript(_, a, i) => {
+            Expression::ArraySubscript(_, _, a, i) => {
                 let array = self
                     .expression(a, vartab, function, runtime)
                     .into_pointer_value();
@@ -1418,7 +1421,7 @@ impl<'a> Contract<'a> {
                     .storage_string_length(&self, function, slot_ptr)
                     .into()
             }
-            Expression::DynamicArraySubscript(_, a, elem_ty, i) => {
+            Expression::DynamicArraySubscript(_, elem_ty, a, i) => {
                 let array = self
                     .expression(a, vartab, function, runtime)
                     .into_pointer_value();
@@ -1451,7 +1454,7 @@ impl<'a> Contract<'a> {
                     .build_pointer_cast(elem, ty.into_pointer_type(), "elem")
                     .into()
             }
-            Expression::StructMember(_, a, i) => {
+            Expression::StructMember(_, _, a, i) => {
                 let array = self
                     .expression(a, vartab, function, runtime)
                     .into_pointer_value();
@@ -1469,7 +1472,7 @@ impl<'a> Contract<'a> {
                         .into()
                 }
             }
-            Expression::Ternary(_, c, l, r) => {
+            Expression::Ternary(_, _, c, l, r) => {
                 let cond = self
                     .expression(c, vartab, function, runtime)
                     .into_int_value();
@@ -1482,7 +1485,7 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_select(cond, left, right, "")
             }
-            Expression::ConstArrayLiteral(_, dims, exprs) => {
+            Expression::ConstArrayLiteral(_, _, dims, exprs) => {
                 // For const arrays (declared with "constant" keyword, we should create a global constant
                 let mut dims = dims.iter();
 
@@ -1620,16 +1623,16 @@ impl<'a> Contract<'a> {
 
                 self.builder.build_load(len, "array_len")
             }
-            Expression::Keccak256(_, exprs) => {
+            Expression::Keccak256(_, _, exprs) => {
                 let mut length = self.context.i32_type().const_zero();
-                let mut values: Vec<(BasicValueEnum, IntValue, resolver::Type)> = Vec::new();
+                let mut values: Vec<(BasicValueEnum, IntValue, ast::Type)> = Vec::new();
 
                 // first we need to calculate the length of the buffer and get the types/lengths
                 for e in exprs {
-                    let v = self.expression(&e.0, vartab, function, runtime);
+                    let v = self.expression(&e, vartab, function, runtime);
 
-                    let len = match e.1 {
-                        resolver::Type::DynamicBytes | resolver::Type::String => {
+                    let len = match e.ty() {
+                        ast::Type::DynamicBytes | ast::Type::String => {
                             // field 0 is the length
                             let array_len = unsafe {
                                 self.builder.build_gep(
@@ -1655,7 +1658,7 @@ impl<'a> Contract<'a> {
 
                     length = self.builder.build_int_add(length, len, "");
 
-                    values.push((v, len, e.1.clone()));
+                    values.push((v, len, e.ty()));
                 }
 
                 //  now allocate a buffer
@@ -1672,7 +1675,7 @@ impl<'a> Contract<'a> {
                     offset = self.builder.build_int_add(offset, len, "");
 
                     match ty {
-                        resolver::Type::DynamicBytes | resolver::Type::String => {
+                        ast::Type::DynamicBytes | ast::Type::String => {
                             let data = unsafe {
                                 self.builder.build_gep(
                                     v.into_pointer_value(),
@@ -1733,7 +1736,7 @@ impl<'a> Contract<'a> {
                     .left()
                     .unwrap()
             }
-            Expression::StringConcat(_, l, r) => {
+            Expression::StringConcat(_, _, l, r) => {
                 let (left, left_len) = self.string_location(l, vartab, function, runtime);
                 let (right, right_len) = self.string_location(r, vartab, function, runtime);
 
@@ -1760,15 +1763,15 @@ impl<'a> Contract<'a> {
                     .into()
             }
             Expression::ReturnData(_) => runtime.return_data(self).into(),
-            Expression::GetAddress(_) => runtime.get_address(self).into(),
-            Expression::Balance(_, addr) => {
+            Expression::GetAddress(_, _) => runtime.get_address(self).into(),
+            Expression::Balance(_, _, addr) => {
                 let addr = self
                     .expression(addr, vartab, function, runtime)
                     .into_int_value();
 
                 runtime.balance(self, addr).into()
             }
-            _ => unreachable!(),
+            _ => panic!("{:?} not implemented", e),
         }
     }
 
@@ -1839,17 +1842,17 @@ impl<'a> Contract<'a> {
     /// Recursively load a type from contract storage
     fn storage_load(
         &self,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         slot: &mut IntValue<'a>,
         slot_ptr: PointerValue<'a>,
         function: FunctionValue,
         runtime: &dyn TargetRuntime,
     ) -> BasicValueEnum<'a> {
         match ty {
-            resolver::Type::Ref(ty) => self.storage_load(ty, slot, slot_ptr, function, runtime),
-            resolver::Type::Array(_, dim) => {
+            ast::Type::Ref(ty) => self.storage_load(ty, slot, slot_ptr, function, runtime),
+            ast::Type::Array(_, dim) => {
                 if let Some(d) = &dim[0] {
-                    let llvm_ty = self.llvm_type(ty.deref());
+                    let llvm_ty = self.llvm_type(ty.deref_any());
                     // LLVMSizeOf() produces an i64
                     let size = self.builder.build_int_truncate(
                         llvm_ty.size_of().unwrap(),
@@ -1902,7 +1905,7 @@ impl<'a> Contract<'a> {
                     dest.into()
                 } else {
                     // iterate over dynamic array
-                    let slot_ty = resolver::Type::Uint(256);
+                    let slot_ty = ast::Type::Uint(256);
 
                     let size = self.builder.build_int_truncate(
                         self.storage_load(&slot_ty, slot, slot_ptr, function, runtime)
@@ -1993,8 +1996,8 @@ impl<'a> Contract<'a> {
                     dest.into()
                 }
             }
-            resolver::Type::Struct(n) => {
-                let llvm_ty = self.llvm_type(ty.deref());
+            ast::Type::Struct(n) => {
+                let llvm_ty = self.llvm_type(ty.deref_any());
                 // LLVMSizeOf() produces an i64
                 let size = self.builder.build_int_truncate(
                     llvm_ty.size_of().unwrap(),
@@ -2039,7 +2042,7 @@ impl<'a> Contract<'a> {
 
                 dest.into()
             }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::String | ast::Type::DynamicBytes => {
                 self.builder.build_store(slot_ptr, *slot);
 
                 let ret = runtime.get_storage_string(&self, function, slot_ptr);
@@ -2059,7 +2062,7 @@ impl<'a> Contract<'a> {
                     &self,
                     function,
                     slot_ptr,
-                    self.llvm_type(ty.deref()).into_int_type(),
+                    self.llvm_type(ty.deref_any()).into_int_type(),
                 );
 
                 *slot = self.builder.build_int_add(
@@ -2076,15 +2079,15 @@ impl<'a> Contract<'a> {
     /// Recursively store a type to contract storage
     fn storage_store(
         &self,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         slot: &mut IntValue<'a>,
         slot_ptr: PointerValue<'a>,
         dest: BasicValueEnum<'a>,
         function: FunctionValue<'a>,
         runtime: &dyn TargetRuntime,
     ) {
-        match ty.deref() {
-            resolver::Type::Array(_, dim) => {
+        match ty.deref_any() {
+            ast::Type::Array(_, dim) => {
                 if let Some(d) = &dim[0] {
                     let ty = ty.array_deref();
 
@@ -2138,7 +2141,7 @@ impl<'a> Contract<'a> {
                         )
                         .into_int_value();
 
-                    let slot_ty = resolver::Type::Uint(256);
+                    let slot_ty = ast::Type::Uint(256);
 
                     // details about our array elements
                     let elem_ty = self.llvm_type(&ty.array_elem());
@@ -2252,7 +2255,7 @@ impl<'a> Contract<'a> {
                     );
                 }
             }
-            resolver::Type::Struct(n) => {
+            ast::Type::Struct(n) => {
                 for (i, field) in self.ns.structs[*n].fields.iter().enumerate() {
                     let mut elem = unsafe {
                         self.builder.build_gep(
@@ -2283,7 +2286,7 @@ impl<'a> Contract<'a> {
                     }
                 }
             }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::String | ast::Type::DynamicBytes => {
                 runtime.set_storage_string(&self, function, slot_ptr, dest.into_pointer_value());
             }
             _ => {
@@ -2309,14 +2312,14 @@ impl<'a> Contract<'a> {
     /// Recursively clear contract storage
     fn storage_clear(
         &self,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         slot: &mut IntValue<'a>,
         slot_ptr: PointerValue<'a>,
         function: FunctionValue<'a>,
         runtime: &dyn TargetRuntime,
     ) {
-        match ty.deref() {
-            resolver::Type::Array(_, dim) => {
+        match ty.deref_any() {
+            ast::Type::Array(_, dim) => {
                 let ty = ty.array_deref();
 
                 if let Some(d) = &dim[0] {
@@ -2384,16 +2387,10 @@ impl<'a> Contract<'a> {
                     );
 
                     // clear length itself
-                    self.storage_clear(
-                        &resolver::Type::Uint(256),
-                        slot,
-                        slot_ptr,
-                        function,
-                        runtime,
-                    );
+                    self.storage_clear(&ast::Type::Uint(256), slot, slot_ptr, function, runtime);
                 }
             }
-            resolver::Type::Struct(n) => {
+            ast::Type::Struct(n) => {
                 for (_, field) in self.ns.structs[*n].fields.iter().enumerate() {
                     self.storage_clear(&field.ty, slot, slot_ptr, function, runtime);
 
@@ -2406,7 +2403,7 @@ impl<'a> Contract<'a> {
                     }
                 }
             }
-            resolver::Type::Mapping(_, _) => {
+            ast::Type::Mapping(_, _) => {
                 // nothing to do, step over it
             }
             _ => {
@@ -2431,7 +2428,7 @@ impl<'a> Contract<'a> {
     }
 
     /// Emit function prototype
-    fn declare_function(&self, fname: &str, f: &resolver::FunctionDecl) -> FunctionValue<'a> {
+    fn declare_function(&self, fname: &str, f: &ast::Function) -> FunctionValue<'a> {
         // function parameters
         let mut args = f
             .params
@@ -2459,23 +2456,23 @@ impl<'a> Contract<'a> {
     /// Emit function body
     fn define_function(
         &self,
-        resolver_func: &resolver::FunctionDecl,
+        codegen_func: &ast::Function,
         function: FunctionValue<'a>,
         runtime: &mut dyn TargetRuntime,
     ) {
-        let cfg = match resolver_func.cfg {
+        let cfg = match codegen_func.cfg {
             Some(ref cfg) => cfg,
             None => panic!(),
         };
 
-        self.emit_cfg(cfg, Some(resolver_func), function, runtime);
+        self.emit_cfg(cfg, Some(codegen_func), function, runtime);
     }
 
     #[allow(clippy::cognitive_complexity)]
     fn emit_cfg(
         &self,
         cfg: &cfg::ControlFlowGraph,
-        resolver_function: Option<&resolver::FunctionDecl>,
+        codegen_function: Option<&ast::Function>,
         function: FunctionValue<'a>,
         runtime: &mut dyn TargetRuntime,
     ) {
@@ -2572,7 +2569,7 @@ impl<'a> Contract<'a> {
                             .build_return(Some(&self.context.i32_type().const_zero()));
                     }
                     cfg::Instr::Return { value } => {
-                        let returns_offset = resolver_function.unwrap().params.len();
+                        let returns_offset = codegen_function.unwrap().params.len();
                         for (i, val) in value.iter().enumerate() {
                             let arg = function.get_nth_param((returns_offset + i) as u32).unwrap();
                             let retval = self.expression(val, &w.vars, function, runtime);
@@ -2591,7 +2588,10 @@ impl<'a> Contract<'a> {
                         self.expression(expr, &w.vars, function, runtime);
                     }
                     cfg::Instr::Constant { res, constant } => {
-                        let const_expr = &self.contract.constants[*constant];
+                        let const_expr = self.contract.variables[*constant]
+                            .initializer
+                            .as_ref()
+                            .unwrap();
                         let value_ref = self.expression(const_expr, &w.vars, function, runtime);
 
                         w.vars[*res].value = value_ref;
@@ -2737,9 +2737,10 @@ impl<'a> Contract<'a> {
                             false,
                             function,
                             &[v],
-                            &[resolver::Parameter {
+                            &[ast::Parameter {
+                                loc: pt::Loc(0, 0),
                                 name: "error".to_owned(),
-                                ty: resolver::Type::String,
+                                ty: ast::Type::String,
                             }],
                         );
 
@@ -3190,7 +3191,7 @@ impl<'a> Contract<'a> {
     /// to the fallback function or receive function, if any.
     pub fn emit_function_dispatch<F>(
         &self,
-        resolver_functions: &[resolver::FunctionDecl],
+        codegen_functions: &[ast::Function],
         function_ty: pt::FunctionTy,
         functions: &[FunctionValue<'a>],
         argsdata: inkwell::values::PointerValue<'a>,
@@ -3200,7 +3201,7 @@ impl<'a> Contract<'a> {
         runtime: &dyn TargetRuntime,
         nonpayable: F,
     ) where
-        F: Fn(&resolver::FunctionDecl) -> bool,
+        F: Fn(&ast::Function) -> bool,
     {
         // create start function
         let no_function_matched = match fallback {
@@ -3243,7 +3244,7 @@ impl<'a> Contract<'a> {
 
         let mut cases = Vec::new();
 
-        for (i, f) in resolver_functions
+        for (i, f) in codegen_functions
             .iter()
             .enumerate()
             .filter(|f| f.1.ty == function_ty)
@@ -4056,37 +4057,35 @@ impl<'a> Contract<'a> {
     }
 
     /// Return the llvm type for a variable holding the type, not the type itself
-    fn llvm_var(&self, ty: &resolver::Type) -> BasicTypeEnum<'a> {
+    fn llvm_var(&self, ty: &ast::Type) -> BasicTypeEnum<'a> {
         let llvm_ty = self.llvm_type(ty);
-        match ty.deref_nonstorage() {
-            resolver::Type::Struct(_)
-            | resolver::Type::Array(_, _)
-            | resolver::Type::DynamicBytes
-            | resolver::Type::String => {
-                llvm_ty.ptr_type(AddressSpace::Generic).as_basic_type_enum()
-            }
+        match ty.deref() {
+            ast::Type::Struct(_)
+            | ast::Type::Array(_, _)
+            | ast::Type::DynamicBytes
+            | ast::Type::String => llvm_ty.ptr_type(AddressSpace::Generic).as_basic_type_enum(),
             _ => llvm_ty,
         }
     }
 
     /// Return the llvm type for the resolved type.
-    fn llvm_type(&self, ty: &resolver::Type) -> BasicTypeEnum<'a> {
+    fn llvm_type(&self, ty: &ast::Type) -> BasicTypeEnum<'a> {
         match ty {
-            resolver::Type::Bool => BasicTypeEnum::IntType(self.context.bool_type()),
-            resolver::Type::Int(n) | resolver::Type::Uint(n) => {
+            ast::Type::Bool => BasicTypeEnum::IntType(self.context.bool_type()),
+            ast::Type::Int(n) | ast::Type::Uint(n) => {
                 BasicTypeEnum::IntType(self.context.custom_width_int_type(*n as u32))
             }
-            resolver::Type::Contract(_) | resolver::Type::Address(_) => {
+            ast::Type::Contract(_) | ast::Type::Address(_) => {
                 BasicTypeEnum::IntType(self.address_type())
             }
-            resolver::Type::Bytes(n) => {
+            ast::Type::Bytes(n) => {
                 BasicTypeEnum::IntType(self.context.custom_width_int_type(*n as u32 * 8))
             }
-            resolver::Type::Enum(n) => self.llvm_type(&self.ns.enums[*n].ty),
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::Enum(n) => self.llvm_type(&self.ns.enums[*n].ty),
+            ast::Type::String | ast::Type::DynamicBytes => {
                 self.module.get_type("struct.vector").unwrap()
             }
-            resolver::Type::Array(base_ty, dims) => {
+            ast::Type::Array(base_ty, dims) => {
                 let ty = self.llvm_var(base_ty);
 
                 let mut dims = dims.iter();
@@ -4105,7 +4104,7 @@ impl<'a> Contract<'a> {
 
                 BasicTypeEnum::ArrayType(aty)
             }
-            resolver::Type::Struct(n) => self
+            ast::Type::Struct(n) => self
                 .context
                 .struct_type(
                     &self.ns.structs[*n]
@@ -4116,15 +4115,15 @@ impl<'a> Contract<'a> {
                     false,
                 )
                 .as_basic_type_enum(),
-            resolver::Type::Undef => unreachable!(),
-            resolver::Type::Mapping(_, _) => unreachable!(),
-            resolver::Type::Ref(r) => self
+            ast::Type::Mapping(_, _) => unreachable!(),
+            ast::Type::Ref(r) => self
                 .llvm_type(r)
                 .ptr_type(AddressSpace::Generic)
                 .as_basic_type_enum(),
-            resolver::Type::StorageRef(_) => {
+            ast::Type::StorageRef(_) => {
                 BasicTypeEnum::IntType(self.context.custom_width_int_type(256))
             }
+            _ => unreachable!(),
         }
     }
 

+ 6 - 6
src/emit/sabre.rs

@@ -1,6 +1,6 @@
+use codegen::cfg::HashTy;
 use parser::pt;
-use resolver;
-use resolver::cfg::HashTy;
+use sema::ast;
 use std::str;
 
 use inkwell::context::Context;
@@ -21,8 +21,8 @@ pub struct SabreTarget {
 impl SabreTarget {
     pub fn build<'a>(
         context: &'a Context,
-        contract: &'a resolver::Contract,
-        ns: &'a resolver::Namespace,
+        contract: &'a ast::Contract,
+        ns: &'a ast::Namespace,
         filename: &'a str,
         opt: OptimizationLevel,
     ) -> Contract<'a> {
@@ -582,7 +582,7 @@ impl TargetRuntime for SabreTarget {
         load: bool,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
         let mut offset = contract.context.i32_type().const_int(
             spec.iter()
@@ -698,7 +698,7 @@ impl TargetRuntime for SabreTarget {
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
         length: IntValue<'b>,
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) {
         self.abi
             .decode(contract, function, args, data, length, spec);

+ 70 - 75
src/emit/substrate.rs

@@ -1,4 +1,5 @@
 use blake2_rfc;
+use codegen::cfg::HashTy;
 use inkwell::context::Context;
 use inkwell::module::Linkage;
 use inkwell::types::{BasicType, IntType};
@@ -8,8 +9,7 @@ use inkwell::IntPredicate;
 use inkwell::OptimizationLevel;
 use num_traits::ToPrimitive;
 use parser::pt;
-use resolver;
-use resolver::cfg::HashTy;
+use sema::ast;
 use std::collections::HashMap;
 
 use super::{Contract, TargetRuntime};
@@ -21,8 +21,8 @@ pub struct SubstrateTarget {
 impl SubstrateTarget {
     pub fn build<'a>(
         context: &'a Context,
-        contract: &'a resolver::Contract,
-        ns: &'a resolver::Namespace,
+        contract: &'a ast::Contract,
+        ns: &'a ast::Namespace,
         filename: &'a str,
         opt: OptimizationLevel,
     ) -> Contract<'a> {
@@ -491,11 +491,11 @@ impl SubstrateTarget {
     fn decode_primitive<'b>(
         &self,
         contract: &Contract<'b>,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         src: PointerValue<'b>,
     ) -> (BasicValueEnum<'b>, u64) {
         match ty {
-            resolver::Type::Bool => {
+            ast::Type::Bool => {
                 let val = contract.builder.build_int_compare(
                     IntPredicate::EQ,
                     contract
@@ -507,12 +507,12 @@ impl SubstrateTarget {
                 );
                 (val.into(), 1)
             }
-            resolver::Type::Contract(_)
-            | resolver::Type::Address(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Int(_) => {
+            ast::Type::Contract(_)
+            | ast::Type::Address(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Int(_) => {
                 let bits = match ty {
-                    resolver::Type::Uint(n) | resolver::Type::Int(n) => *n as u32,
+                    ast::Type::Uint(n) | ast::Type::Int(n) => *n as u32,
                     _ => contract.ns.address_length as u32 * 8,
                 };
 
@@ -531,7 +531,7 @@ impl SubstrateTarget {
 
                 (val, len)
             }
-            resolver::Type::Bytes(len) => {
+            ast::Type::Bytes(len) => {
                 let int_type = contract.context.custom_width_int_type(*len as u32 * 8);
 
                 let buf = contract.builder.build_alloca(int_type, "buf");
@@ -607,17 +607,17 @@ impl SubstrateTarget {
         &self,
         contract: &Contract<'b>,
         function: FunctionValue,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         data: &mut PointerValue<'b>,
         end: PointerValue<'b>,
     ) -> BasicValueEnum<'b> {
         match &ty {
-            resolver::Type::Bool
-            | resolver::Type::Address(_)
-            | resolver::Type::Contract(_)
-            | resolver::Type::Int(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Bytes(_) => {
+            ast::Type::Bool
+            | ast::Type::Address(_)
+            | ast::Type::Contract(_)
+            | ast::Type::Int(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Bytes(_) => {
                 let (arg, arglen) = self.decode_primitive(contract, ty, *data);
 
                 *data = unsafe {
@@ -632,11 +632,11 @@ impl SubstrateTarget {
 
                 arg
             }
-            resolver::Type::Enum(n) => {
+            ast::Type::Enum(n) => {
                 self.decode_ty(contract, function, &contract.ns.enums[*n].ty, data, end)
             }
-            resolver::Type::Struct(n) => {
-                let llvm_ty = contract.llvm_type(ty.deref());
+            ast::Type::Struct(n) => {
+                let llvm_ty = contract.llvm_type(ty.deref_any());
 
                 let size = llvm_ty
                     .size_of()
@@ -680,9 +680,9 @@ impl SubstrateTarget {
 
                 dest.into()
             }
-            resolver::Type::Array(_, dim) => {
+            ast::Type::Array(_, dim) => {
                 if let Some(d) = &dim[0] {
-                    let llvm_ty = contract.llvm_type(ty.deref());
+                    let llvm_ty = contract.llvm_type(ty.deref_any());
 
                     let size = llvm_ty
                         .size_of()
@@ -815,7 +815,7 @@ impl SubstrateTarget {
                     v.into()
                 }
             }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::String | ast::Type::DynamicBytes => {
                 let from = contract.builder.build_alloca(
                     contract.context.i8_type().ptr_type(AddressSpace::Generic),
                     "from",
@@ -854,10 +854,8 @@ impl SubstrateTarget {
                     )
                     .into()
             }
-            resolver::Type::Undef => unreachable!(),
-            resolver::Type::StorageRef(_) => unreachable!(),
-            resolver::Type::Mapping(_, _) => unreachable!(),
-            resolver::Type::Ref(ty) => self.decode_ty(contract, function, ty, data, end),
+            ast::Type::Ref(ty) => self.decode_ty(contract, function, ty, data, end),
+            _ => unreachable!(),
         }
     }
 
@@ -866,12 +864,12 @@ impl SubstrateTarget {
         &self,
         contract: &Contract,
         load: bool,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         dest: PointerValue,
         arg: BasicValueEnum,
     ) -> u64 {
         match ty {
-            resolver::Type::Bool => {
+            ast::Type::Bool => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -888,12 +886,12 @@ impl SubstrateTarget {
                 );
                 1
             }
-            resolver::Type::Contract(_)
-            | resolver::Type::Address(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Int(_) => {
+            ast::Type::Contract(_)
+            | ast::Type::Address(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Int(_) => {
                 let len = match ty {
-                    resolver::Type::Uint(n) | resolver::Type::Int(n) => *n as u64 / 8,
+                    ast::Type::Uint(n) | ast::Type::Int(n) => *n as u64 / 8,
                     _ => contract.ns.address_length as u64,
                 };
 
@@ -916,7 +914,7 @@ impl SubstrateTarget {
 
                 len
             }
-            resolver::Type::Bytes(n) => {
+            ast::Type::Bytes(n) => {
                 let val = if load {
                     arg.into_pointer_value()
                 } else {
@@ -964,17 +962,17 @@ impl SubstrateTarget {
         contract: &Contract<'a>,
         load: bool,
         function: FunctionValue,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         arg: BasicValueEnum<'a>,
         data: &mut PointerValue<'a>,
     ) {
         match &ty {
-            resolver::Type::Bool
-            | resolver::Type::Address(_)
-            | resolver::Type::Contract(_)
-            | resolver::Type::Int(_)
-            | resolver::Type::Uint(_)
-            | resolver::Type::Bytes(_) => {
+            ast::Type::Bool
+            | ast::Type::Address(_)
+            | ast::Type::Contract(_)
+            | ast::Type::Int(_)
+            | ast::Type::Uint(_)
+            | ast::Type::Bytes(_) => {
                 let arglen = self.encode_primitive(contract, load, ty, *data, arg);
 
                 *data = unsafe {
@@ -985,10 +983,10 @@ impl SubstrateTarget {
                     )
                 };
             }
-            resolver::Type::Enum(n) => {
+            ast::Type::Enum(n) => {
                 self.encode_primitive(contract, load, &contract.ns.enums[*n].ty, *data, arg);
             }
-            resolver::Type::Array(_, dim) => {
+            ast::Type::Array(_, dim) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -1019,7 +1017,7 @@ impl SubstrateTarget {
                                 contract,
                                 true,
                                 function,
-                                &ty.deref(),
+                                &ty.deref_any(),
                                 elem.into(),
                                 data,
                             );
@@ -1096,7 +1094,7 @@ impl SubstrateTarget {
                                 contract,
                                 true,
                                 function,
-                                &ty.deref(),
+                                &ty.deref_any(),
                                 elem.into(),
                                 data,
                             );
@@ -1104,7 +1102,7 @@ impl SubstrateTarget {
                     );
                 }
             }
-            resolver::Type::Struct(n) => {
+            ast::Type::Struct(n) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -1126,13 +1124,10 @@ impl SubstrateTarget {
                     self.encode_ty(contract, true, function, &field.ty, elem.into(), data);
                 }
             }
-            resolver::Type::Undef => unreachable!(),
-            resolver::Type::StorageRef(_) => unreachable!(),
-            resolver::Type::Mapping(_, _) => unreachable!(),
-            resolver::Type::Ref(ty) => {
+            ast::Type::Ref(ty) => {
                 self.encode_ty(contract, load, function, ty, arg, data);
             }
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::String | ast::Type::DynamicBytes => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -1164,6 +1159,7 @@ impl SubstrateTarget {
                     .unwrap()
                     .into_pointer_value();
             }
+            _ => unreachable!(),
         };
     }
 
@@ -1175,24 +1171,24 @@ impl SubstrateTarget {
         &self,
         arg: BasicValueEnum<'a>,
         load: bool,
-        ty: &resolver::Type,
+        ty: &ast::Type,
         function: FunctionValue,
         contract: &Contract<'a>,
     ) -> IntValue<'a> {
         match ty {
-            resolver::Type::Bool => contract.context.i32_type().const_int(1, false),
-            resolver::Type::Uint(n) | resolver::Type::Int(n) => {
+            ast::Type::Bool => contract.context.i32_type().const_int(1, false),
+            ast::Type::Uint(n) | ast::Type::Int(n) => {
                 contract.context.i32_type().const_int(*n as u64 / 8, false)
             }
-            resolver::Type::Bytes(n) => contract.context.i32_type().const_int(*n as u64, false),
-            resolver::Type::Address(_) | resolver::Type::Contract(_) => contract
+            ast::Type::Bytes(n) => contract.context.i32_type().const_int(*n as u64, false),
+            ast::Type::Address(_) | ast::Type::Contract(_) => contract
                 .context
                 .i32_type()
                 .const_int(contract.ns.address_length as u64, false),
-            resolver::Type::Enum(n) => {
+            ast::Type::Enum(n) => {
                 self.encoded_length(arg, load, &contract.ns.enums[*n].ty, function, contract)
             }
-            resolver::Type::Struct(n) => {
+            ast::Type::Struct(n) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -1222,7 +1218,7 @@ impl SubstrateTarget {
 
                 sum
             }
-            resolver::Type::Array(_, dims) => {
+            ast::Type::Array(_, dims) => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -1360,11 +1356,8 @@ impl SubstrateTarget {
                     )
                 }
             }
-            resolver::Type::Undef => unreachable!(),
-            resolver::Type::StorageRef(_) => unreachable!(),
-            resolver::Type::Mapping(_, _) => unreachable!(),
-            resolver::Type::Ref(r) => self.encoded_length(arg, load, r, function, contract),
-            resolver::Type::String | resolver::Type::DynamicBytes => {
+            ast::Type::Ref(r) => self.encoded_length(arg, load, r, function, contract),
+            ast::Type::String | ast::Type::DynamicBytes => {
                 let arg = if load {
                     contract.builder.build_load(arg.into_pointer_value(), "")
                 } else {
@@ -1394,6 +1387,7 @@ impl SubstrateTarget {
                     "",
                 )
             }
+            _ => unreachable!(),
         }
     }
 
@@ -1883,7 +1877,7 @@ impl TargetRuntime for SubstrateTarget {
         args: &mut Vec<BasicValueEnum<'b>>,
         data: PointerValue<'b>,
         datalength: IntValue<'b>,
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) {
         let mut argsdata = contract.builder.build_pointer_cast(
             data,
@@ -1910,7 +1904,7 @@ impl TargetRuntime for SubstrateTarget {
         load: bool,
         function: FunctionValue,
         args: &[BasicValueEnum<'b>],
-        spec: &[resolver::Parameter],
+        spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
         // first calculate how much memory we need to allocate
         let mut length = contract.context.i32_type().const_zero();
@@ -2015,6 +2009,7 @@ impl TargetRuntime for SubstrateTarget {
         salt: Option<IntValue<'b>>,
     ) {
         let resolver_contract = &contract.ns.contracts[contract_no];
+
         let constructor = &resolver_contract
             .functions
             .iter()
@@ -2026,7 +2021,7 @@ impl TargetRuntime for SubstrateTarget {
         let mut params = constructor.params.to_vec();
 
         // salt
-        let salt_ty = resolver::Type::Uint(256);
+        let salt_ty = ast::Type::Uint(256);
 
         if let Some(salt) = salt {
             args.push(salt.into());
@@ -2063,7 +2058,8 @@ impl TargetRuntime for SubstrateTarget {
             args.push(contract.builder.build_load(salt, "salt"));
         }
 
-        params.push(resolver::Parameter {
+        params.push(ast::Parameter {
+            loc: pt::Loc(0, 0),
             ty: salt_ty,
             name: "salt".to_string(),
         });
@@ -2589,10 +2585,9 @@ impl TargetRuntime for SubstrateTarget {
         );
 
         // bytes32 needs to reverse bytes
-        let temp = contract.builder.build_alloca(
-            contract.llvm_type(&resolver::Type::Bytes(hashlen as u8)),
-            "hash",
-        );
+        let temp = contract
+            .builder
+            .build_alloca(contract.llvm_type(&ast::Type::Bytes(hashlen as u8)), "hash");
 
         contract.builder.build_call(
             contract.module.get_function("__beNtoleN").unwrap(),

+ 23 - 18
src/lib.rs

@@ -15,12 +15,12 @@ extern crate tiny_keccak;
 extern crate unicode_xid;
 
 pub mod abi;
+pub mod codegen;
+mod emit;
 pub mod link;
 pub mod output;
-
-mod emit;
 mod parser;
-mod resolver;
+mod sema;
 
 use inkwell::OptimizationLevel;
 use std::fmt;
@@ -61,7 +61,7 @@ pub fn compile(
 ) -> (Vec<(Vec<u8>, String)>, Vec<output::Output>) {
     let ctx = inkwell::context::Context::create();
 
-    let ast = match parser::parse(src) {
+    let pt = match parser::parse(src) {
         Ok(s) => s,
         Err(errors) => {
             return (Vec::new(), errors);
@@ -69,12 +69,16 @@ pub fn compile(
     };
 
     // resolve
-    let (ns, errors) = match resolver::resolver(ast, target) {
-        (None, errors) => {
-            return (Vec::new(), errors);
-        }
-        (Some(ns), errors) => (ns, errors),
-    };
+    let mut ns = sema::sema(pt, target);
+
+    if output::any_errors(&ns.diagnostics) {
+        return (Vec::new(), ns.diagnostics);
+    }
+
+    // codegen all the contracts
+    for contract_no in 0..ns.contracts.len() {
+        codegen::codegen(contract_no, &mut ns);
+    }
 
     let results = (0..ns.contracts.len())
         .map(|c| {
@@ -89,7 +93,7 @@ pub fn compile(
         })
         .collect();
 
-    (results, errors)
+    (results, ns.diagnostics)
 }
 
 /// Parse and resolve the Solidity source code provided in src, for the target chain as specified in target.
@@ -97,17 +101,18 @@ pub fn compile(
 /// informational messages like `found contact N`.
 ///
 /// Note that multiple contracts can be specified in on solidity source file.
-pub fn parse_and_resolve(
-    src: &str,
-    target: Target,
-) -> (Option<resolver::Namespace>, Vec<output::Output>) {
-    let ast = match parser::parse(src) {
+pub fn parse_and_resolve(src: &str, target: Target) -> sema::ast::Namespace {
+    let pt = match parser::parse(src) {
         Ok(s) => s,
         Err(errors) => {
-            return (None, errors);
+            let mut ns = sema::ast::Namespace::new(target, 32);
+
+            ns.diagnostics = errors;
+
+            return ns;
         }
     };
 
     // resolve
-    resolver::resolver(ast, target)
+    sema::sema(pt, target)
 }

+ 4 - 0
src/parser/mod.rs

@@ -97,6 +97,7 @@ mod test {
                         },
                         fields: vec![
                             VariableDeclaration {
+                                loc: Loc(81, 92),
                                 ty: Expression::Type(Loc(81, 85), Type::Bool),
                                 storage: None,
                                 name: Identifier {
@@ -105,6 +106,7 @@ mod test {
                                 },
                             },
                             VariableDeclaration {
+                                loc: Loc(118, 129),
                                 ty: Expression::Type(Loc(118, 122), Type::Uint(256)),
                                 storage: None,
                                 name: Identifier {
@@ -113,6 +115,7 @@ mod test {
                                 },
                             },
                             VariableDeclaration {
+                                loc: Loc(155, 169),
                                 ty: Expression::Type(Loc(155, 161), Type::Bytes(2)),
                                 storage: None,
                                 name: Identifier {
@@ -121,6 +124,7 @@ mod test {
                                 },
                             },
                             VariableDeclaration {
+                                loc: Loc(195, 209),
                                 ty: Expression::Type(Loc(195, 202), Type::Bytes(32)),
                                 storage: None,
                                 name: Identifier {

+ 2 - 0
src/parser/pt.rs

@@ -82,6 +82,7 @@ impl fmt::Display for StorageLocation {
 
 #[derive(Debug, PartialEq, Clone)]
 pub struct VariableDeclaration {
+    pub loc: Loc,
     pub ty: Expression,
     pub storage: Option<StorageLocation>,
     pub name: Identifier,
@@ -317,6 +318,7 @@ impl Expression {
 
 #[derive(Clone, Debug, PartialEq)]
 pub struct Parameter {
+    pub loc: Loc,
     pub ty: Expression,
     pub storage: Option<StorageLocation>,
     pub name: Option<Identifier>,

+ 6 - 5
src/parser/solidity.lalrpop

@@ -63,8 +63,8 @@ Identifier: Identifier = {
 }
 
 VariableDeclaration: VariableDeclaration = {
-    <ty:Precedence0> <storage:StorageLocation?> <name:Identifier> => VariableDeclaration {
-        ty, storage, name
+    <l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:Identifier> <r:@R> => VariableDeclaration {
+        loc: Loc(l, r), ty, storage, name
     },
 }
 
@@ -282,7 +282,7 @@ Precedence0: Expression = {
     },
     <l:@L> <a:ParameterList> <r:@R> => {
         if a.len() == 1 {
-            if let Some(Parameter{ ty, storage: None, name: None }) = &a[0].1 {
+            if let Some(Parameter{ ty, storage: None, name: None, .. }) = &a[0].1 {
                 // this means "(" Expression ")"
                 return ty.clone();
             }
@@ -325,8 +325,9 @@ HexLiteral: HexLiteral = {
 // and as an added bonus we can generate error messages about missing parameters/returns
 // to functions
 Parameter: Parameter = {
-    <ty:Expression> <storage:StorageLocation?> <name:Identifier?> => {
-        Parameter{ty, storage, name}
+    <l:@L> <ty:Expression> <storage:StorageLocation?> <name:Identifier?> <r:@R> => {
+        let loc = Loc(l, r);
+        Parameter{loc, ty, storage, name}
     }
 }
 

Різницю між файлами не показано, бо вона завелика
+ 117 - 117
src/parser/solidity.rs


+ 0 - 482
src/resolver/builtin.rs

@@ -1,482 +0,0 @@
-use super::cfg::{ControlFlowGraph, HashTy, Instr, Vartable};
-use super::expression::Expression;
-use super::{FunctionDecl, Namespace, Parameter};
-use crate::Target;
-use parser::pt;
-use resolver;
-
-pub fn add_builtin_function(ns: &mut Namespace, contract_no: usize) {
-    add_assert(ns, contract_no);
-    add_print(ns, contract_no);
-    add_revert(ns, contract_no);
-    add_require(ns, contract_no);
-    add_selfdestruct(ns, contract_no);
-    add_crypto_hash(ns, contract_no);
-}
-
-fn add_assert(ns: &mut Namespace, contract_no: usize) {
-    let id = pt::Identifier {
-        loc: pt::Loc(0, 0),
-        name: "assert".to_owned(),
-    };
-
-    let mut assert = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "assert".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![Parameter {
-            name: "arg0".to_owned(),
-            ty: resolver::Type::Bool,
-        }],
-        vec![],
-        ns,
-    );
-
-    let mut errors = Vec::new();
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    let true_ = cfg.new_basic_block("noassert".to_owned());
-    let false_ = cfg.new_basic_block("doassert".to_owned());
-
-    cfg.add(
-        &mut vartab,
-        Instr::BranchCond {
-            cond: Expression::FunctionArg(pt::Loc(0, 0), 0),
-            true_,
-            false_,
-        },
-    );
-
-    cfg.set_basic_block(true_);
-    cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
-
-    cfg.set_basic_block(false_);
-    cfg.add(&mut vartab, Instr::AssertFailure { expr: None });
-
-    cfg.vars = vartab.drain();
-
-    assert.cfg = Some(Box::new(cfg));
-
-    let pos = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(assert);
-
-    ns.add_symbol(
-        Some(contract_no),
-        &id,
-        resolver::Symbol::Function(vec![(id.loc, pos)]),
-        &mut errors,
-    );
-}
-
-fn add_print(ns: &mut Namespace, contract_no: usize) {
-    let id = pt::Identifier {
-        loc: pt::Loc(0, 0),
-        name: "print".to_owned(),
-    };
-
-    let mut assert = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "print".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![Parameter {
-            name: "arg0".to_owned(),
-            ty: resolver::Type::String,
-        }],
-        vec![],
-        ns,
-    );
-
-    let mut errors = Vec::new();
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    cfg.add(
-        &mut vartab,
-        Instr::Print {
-            expr: Expression::FunctionArg(pt::Loc(0, 0), 0),
-        },
-    );
-    cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
-    cfg.vars = vartab.drain();
-
-    assert.cfg = Some(Box::new(cfg));
-
-    let pos = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(assert);
-
-    ns.add_symbol(
-        Some(contract_no),
-        &id,
-        resolver::Symbol::Function(vec![(id.loc, pos)]),
-        &mut errors,
-    );
-}
-
-fn add_require(ns: &mut Namespace, contract_no: usize) {
-    let id = pt::Identifier {
-        loc: pt::Loc(0, 0),
-        name: "require".to_owned(),
-    };
-
-    let mut require = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "require".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![
-            Parameter {
-                name: "condition".to_owned(),
-                ty: resolver::Type::Bool,
-            },
-            Parameter {
-                name: "ReasonCode".to_owned(),
-                ty: resolver::Type::String,
-            },
-        ],
-        vec![],
-        ns,
-    );
-
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    let true_ = cfg.new_basic_block("noassert".to_owned());
-    let false_ = cfg.new_basic_block("doassert".to_owned());
-
-    cfg.add(
-        &mut vartab,
-        Instr::BranchCond {
-            cond: Expression::FunctionArg(pt::Loc(0, 0), 0),
-            true_,
-            false_,
-        },
-    );
-
-    cfg.set_basic_block(true_);
-    cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
-
-    cfg.set_basic_block(false_);
-    cfg.add(
-        &mut vartab,
-        Instr::AssertFailure {
-            expr: Some(Expression::FunctionArg(pt::Loc(0, 0), 1)),
-        },
-    );
-
-    cfg.vars = vartab.drain();
-
-    require.cfg = Some(Box::new(cfg));
-
-    let pos_with_reason = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(require);
-
-    let mut require = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "require".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![Parameter {
-            name: "condition".to_owned(),
-            ty: resolver::Type::Bool,
-        }],
-        vec![],
-        ns,
-    );
-
-    let mut errors = Vec::new();
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    let true_ = cfg.new_basic_block("noassert".to_owned());
-    let false_ = cfg.new_basic_block("doassert".to_owned());
-
-    cfg.add(
-        &mut vartab,
-        Instr::BranchCond {
-            cond: Expression::FunctionArg(pt::Loc(0, 0), 0),
-            true_,
-            false_,
-        },
-    );
-
-    cfg.set_basic_block(true_);
-    cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
-
-    cfg.set_basic_block(false_);
-    cfg.add(&mut vartab, Instr::AssertFailure { expr: None });
-
-    cfg.vars = vartab.drain();
-
-    require.cfg = Some(Box::new(cfg));
-
-    let pos_without_reason = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(require);
-
-    ns.add_symbol(
-        Some(contract_no),
-        &id,
-        resolver::Symbol::Function(vec![
-            (id.loc, pos_with_reason),
-            (id.loc, pos_without_reason),
-        ]),
-        &mut errors,
-    );
-}
-
-fn add_revert(ns: &mut Namespace, contract_no: usize) {
-    let id = pt::Identifier {
-        loc: pt::Loc(0, 0),
-        name: "revert".to_owned(),
-    };
-
-    let mut revert = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "revert".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![Parameter {
-            name: "ReasonCode".to_owned(),
-            ty: resolver::Type::String,
-        }],
-        vec![],
-        ns,
-    );
-
-    revert.noreturn = true;
-
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    cfg.add(
-        &mut vartab,
-        Instr::AssertFailure {
-            expr: Some(Expression::FunctionArg(pt::Loc(0, 0), 0)),
-        },
-    );
-
-    cfg.vars = vartab.drain();
-
-    revert.cfg = Some(Box::new(cfg));
-
-    let pos_with_arg = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(revert);
-
-    // now add variant with no argument
-    let mut revert = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "revert".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![],
-        vec![],
-        ns,
-    );
-
-    revert.noreturn = true;
-
-    let mut errors = Vec::new();
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    cfg.add(&mut vartab, Instr::AssertFailure { expr: None });
-
-    cfg.vars = vartab.drain();
-
-    revert.cfg = Some(Box::new(cfg));
-
-    let pos_with_no_arg = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(revert);
-
-    ns.add_symbol(
-        Some(contract_no),
-        &id,
-        resolver::Symbol::Function(vec![(id.loc, pos_with_arg), (id.loc, pos_with_no_arg)]),
-        &mut errors,
-    );
-}
-
-fn add_selfdestruct(ns: &mut Namespace, contract_no: usize) {
-    let id = pt::Identifier {
-        loc: pt::Loc(0, 0),
-        name: "selfdestruct".to_owned(),
-    };
-
-    let mut selfdestruct = FunctionDecl::new(
-        pt::Loc(0, 0),
-        "selfdestruct".to_owned(),
-        vec![],
-        pt::FunctionTy::Function,
-        None,
-        None,
-        pt::Visibility::Private(pt::Loc(0, 0)),
-        vec![Parameter {
-            name: "recipient".to_owned(),
-            ty: resolver::Type::Address(true),
-        }],
-        vec![],
-        ns,
-    );
-
-    selfdestruct.noreturn = true;
-
-    let mut errors = Vec::new();
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
-
-    cfg.add(
-        &mut vartab,
-        Instr::SelfDestruct {
-            recipient: Expression::FunctionArg(pt::Loc(0, 0), 0),
-        },
-    );
-    cfg.add(&mut vartab, Instr::Unreachable);
-    cfg.vars = vartab.drain();
-
-    selfdestruct.cfg = Some(Box::new(cfg));
-
-    let pos = ns.contracts[contract_no].functions.len();
-
-    ns.contracts[contract_no].functions.push(selfdestruct);
-
-    ns.add_symbol(
-        Some(contract_no),
-        &id,
-        resolver::Symbol::Function(vec![(id.loc, pos)]),
-        &mut errors,
-    );
-}
-
-fn add_crypto_hash(ns: &mut Namespace, contract_no: usize) {
-    struct HashFunction {
-        function_name: &'static str,
-        hash_ty: HashTy,
-        ret_ty: resolver::Type,
-        target: Option<Target>,
-    };
-
-    for hash in &[
-        HashFunction {
-            function_name: "keccak256",
-            hash_ty: HashTy::Keccak256,
-            ret_ty: resolver::Type::Bytes(32),
-            target: None,
-        },
-        HashFunction {
-            function_name: "ripemd160",
-            hash_ty: HashTy::Ripemd160,
-            ret_ty: resolver::Type::Bytes(20),
-            target: None,
-        },
-        HashFunction {
-            function_name: "sha256",
-            hash_ty: HashTy::Sha256,
-            ret_ty: resolver::Type::Bytes(32),
-            target: None,
-        },
-        HashFunction {
-            function_name: "blake2_128",
-            hash_ty: HashTy::Blake2_128,
-            ret_ty: resolver::Type::Bytes(16),
-            target: Some(Target::Substrate),
-        },
-        HashFunction {
-            function_name: "blake2_256",
-            hash_ty: HashTy::Blake2_256,
-            ret_ty: resolver::Type::Bytes(32),
-            target: Some(Target::Substrate),
-        },
-    ] {
-        if let Some(target) = hash.target {
-            if target != ns.target {
-                continue;
-            }
-        }
-
-        let id = pt::Identifier {
-            loc: pt::Loc(0, 0),
-            name: hash.function_name.to_owned(),
-        };
-
-        let mut assert = FunctionDecl::new(
-            pt::Loc(0, 0),
-            hash.function_name.to_owned(),
-            vec![],
-            pt::FunctionTy::Function,
-            None,
-            None,
-            pt::Visibility::Private(pt::Loc(0, 0)),
-            vec![Parameter {
-                name: "bs".to_owned(),
-                ty: resolver::Type::DynamicBytes,
-            }],
-            vec![Parameter {
-                name: "hash".to_owned(),
-                ty: hash.ret_ty.clone(),
-            }],
-            ns,
-        );
-
-        let mut errors = Vec::new();
-        let mut vartab = Vartable::new();
-        let mut cfg = ControlFlowGraph::new();
-
-        let res = vartab.temp_anonymous(&resolver::Type::Bytes(32));
-
-        cfg.add(
-            &mut vartab,
-            Instr::Hash {
-                res,
-                hash: hash.hash_ty.clone(),
-                expr: Expression::FunctionArg(pt::Loc(0, 0), 0),
-            },
-        );
-        cfg.add(
-            &mut vartab,
-            Instr::Return {
-                value: vec![Expression::Variable(pt::Loc(0, 0), res)],
-            },
-        );
-
-        cfg.vars = vartab.drain();
-
-        assert.cfg = Some(Box::new(cfg));
-
-        let pos = ns.contracts[contract_no].functions.len();
-
-        ns.contracts[contract_no].functions.push(assert);
-
-        ns.add_symbol(
-            Some(contract_no),
-            &id,
-            resolver::Symbol::Function(vec![(id.loc, pos)]),
-            &mut errors,
-        );
-    }
-}

+ 0 - 2352
src/resolver/cfg.rs

@@ -1,2352 +0,0 @@
-use num_bigint::BigInt;
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::collections::LinkedList;
-use std::fmt;
-use std::str;
-
-use hex;
-use output;
-use output::Output;
-use parser::pt;
-use resolver;
-use resolver::expression::{
-    cast, constructor_named_args, expression, function_call_expr, named_function_call_expr, new,
-    parameter_list_to_expr_list, Expression, StringLocation,
-};
-
-#[allow(clippy::large_enum_variant)]
-pub enum Instr {
-    ClearStorage {
-        ty: resolver::Type,
-        storage: Expression,
-    },
-    SetStorage {
-        ty: resolver::Type,
-        local: usize,
-        storage: Expression,
-    },
-    SetStorageBytes {
-        local: usize,
-        storage: Box<Expression>,
-        offset: Box<Expression>,
-    },
-    Set {
-        res: usize,
-        expr: Expression,
-    },
-    Eval {
-        expr: Expression,
-    },
-    Constant {
-        res: usize,
-        constant: usize,
-    },
-    Call {
-        res: Vec<usize>,
-        func: usize,
-        args: Vec<Expression>,
-    },
-    Return {
-        value: Vec<Expression>,
-    },
-    Branch {
-        bb: usize,
-    },
-    BranchCond {
-        cond: Expression,
-        true_: usize,
-        false_: usize,
-    },
-    Store {
-        dest: Expression,
-        pos: usize,
-    },
-    AssertFailure {
-        expr: Option<Expression>,
-    },
-    Print {
-        expr: Expression,
-    },
-    Constructor {
-        success: Option<usize>,
-        res: usize,
-        contract_no: usize,
-        constructor_no: usize,
-        args: Vec<Expression>,
-        value: Option<Expression>,
-        gas: Expression,
-        salt: Option<Expression>,
-    },
-    ExternalCall {
-        success: Option<usize>,
-        address: Expression,
-        contract_no: Option<usize>,
-        function_no: usize,
-        args: Vec<Expression>,
-        value: Expression,
-        gas: Expression,
-    },
-    AbiDecode {
-        res: Vec<usize>,
-        selector: Option<u32>,
-        exception: Option<usize>,
-        tys: Vec<resolver::Parameter>,
-        data: Expression,
-    },
-    Unreachable,
-    SelfDestruct {
-        recipient: Expression,
-    },
-    Hash {
-        res: usize,
-        hash: HashTy,
-        expr: Expression,
-    },
-}
-
-#[derive(Clone, PartialEq)]
-pub enum HashTy {
-    Keccak256,
-    Ripemd160,
-    Sha256,
-    Blake2_256,
-    Blake2_128,
-}
-
-impl fmt::Display for HashTy {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            HashTy::Keccak256 => write!(f, "keccak256"),
-            HashTy::Ripemd160 => write!(f, "ripemd160"),
-            HashTy::Sha256 => write!(f, "sha256"),
-            HashTy::Blake2_128 => write!(f, "blake2_128"),
-            HashTy::Blake2_256 => write!(f, "blake2_256"),
-        }
-    }
-}
-
-pub struct BasicBlock {
-    pub phis: Option<HashSet<usize>>,
-    pub name: String,
-    pub instr: Vec<Instr>,
-}
-
-impl BasicBlock {
-    fn add(&mut self, ins: Instr) {
-        self.instr.push(ins);
-    }
-}
-
-#[derive(Default)]
-pub struct ControlFlowGraph {
-    pub vars: Vec<Variable>,
-    pub bb: Vec<BasicBlock>,
-    current: usize,
-    pub writes_contract_storage: bool,
-}
-
-impl ControlFlowGraph {
-    pub fn new() -> Self {
-        let mut cfg = ControlFlowGraph {
-            vars: Vec::new(),
-            bb: Vec::new(),
-            current: 0,
-            writes_contract_storage: false,
-        };
-
-        cfg.new_basic_block("entry".to_string());
-
-        cfg
-    }
-
-    /// Does this function read from contract storage anywhere in its body
-    pub fn reads_contract_storage(&self) -> bool {
-        self.bb.iter().any(|bb| {
-            bb.instr.iter().any(|instr| match instr {
-                Instr::Eval { expr } | Instr::Set { expr, .. } => expr.reads_contract_storage(),
-                Instr::Return { value } => value.iter().any(|e| e.reads_contract_storage()),
-                Instr::BranchCond { cond, .. } => cond.reads_contract_storage(),
-                Instr::Call { args, .. } => args.iter().any(|e| e.reads_contract_storage()),
-                _ => false,
-            })
-        })
-    }
-
-    pub fn new_basic_block(&mut self, name: String) -> usize {
-        let pos = self.bb.len();
-
-        self.bb.push(BasicBlock {
-            name,
-            instr: Vec::new(),
-            phis: None,
-        });
-
-        pos
-    }
-
-    pub fn set_phis(&mut self, bb: usize, phis: HashSet<usize>) {
-        if !phis.is_empty() {
-            self.bb[bb].phis = Some(phis);
-        }
-    }
-
-    pub fn set_basic_block(&mut self, pos: usize) {
-        self.current = pos;
-    }
-
-    pub fn add(&mut self, vartab: &mut Vartable, ins: Instr) {
-        if let Instr::Set { res, .. } = ins {
-            vartab.set_dirty(res);
-        }
-        self.bb[self.current].add(ins);
-    }
-
-    pub fn expr_to_string(
-        &self,
-        contract: &resolver::Contract,
-        ns: &resolver::Namespace,
-        expr: &Expression,
-    ) -> String {
-        match expr {
-            Expression::FunctionArg(_, pos) => format!("(arg #{})", pos),
-            Expression::BoolLiteral(_, false) => "false".to_string(),
-            Expression::BoolLiteral(_, true) => "true".to_string(),
-            Expression::BytesLiteral(_, s) => format!("hex\"{}\"", hex::encode(s)),
-            Expression::NumberLiteral(_, bits, n) => format!("i{} {}", bits, n.to_str_radix(10)),
-            Expression::StructLiteral(_, _, expr) => format!(
-                "struct {{ {} }}",
-                expr.iter()
-                    .map(|e| self.expr_to_string(contract, ns, e))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::ConstArrayLiteral(_, dims, exprs) => format!(
-                "constant {} [ {} ]",
-                dims.iter().map(|d| format!("[{}]", d)).collect::<String>(),
-                exprs
-                    .iter()
-                    .map(|e| self.expr_to_string(contract, ns, e))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::ArrayLiteral(_, _, dims, exprs) => format!(
-                "{} [ {} ]",
-                dims.iter().map(|d| format!("[{}]", d)).collect::<String>(),
-                exprs
-                    .iter()
-                    .map(|e| self.expr_to_string(contract, ns, e))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::Add(_, l, r) => format!(
-                "({} + {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Subtract(_, l, r) => format!(
-                "({} - {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::BitwiseOr(_, l, r) => format!(
-                "({} | {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::BitwiseAnd(_, l, r) => format!(
-                "({} & {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::BitwiseXor(_, l, r) => format!(
-                "({} ^ {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::ShiftLeft(_, l, r) => format!(
-                "({} << {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::ShiftRight(_, l, r, _) => format!(
-                "({} >> {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Multiply(_, l, r) => format!(
-                "({} * {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::UDivide(_, l, r) | Expression::SDivide(_, l, r) => format!(
-                "({} / {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::UModulo(_, l, r) | Expression::SModulo(_, l, r) => format!(
-                "({} % {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Power(_, l, r) => format!(
-                "({} ** {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Variable(_, res) => format!("%{}", self.vars[*res].id.name),
-            Expression::Load(_, expr) => {
-                format!("(load {})", self.expr_to_string(contract, ns, expr))
-            }
-            Expression::StorageLoad(_, ty, expr) => format!(
-                "({} storage[{}])",
-                ty.to_string(ns),
-                self.expr_to_string(contract, ns, expr)
-            ),
-            Expression::ZeroExt(_, ty, e) => format!(
-                "(zext {} {})",
-                ty.to_string(ns),
-                self.expr_to_string(contract, ns, e)
-            ),
-            Expression::SignExt(_, ty, e) => format!(
-                "(sext {} {})",
-                ty.to_string(ns),
-                self.expr_to_string(contract, ns, e)
-            ),
-            Expression::Trunc(_, ty, e) => format!(
-                "(trunc {} {})",
-                ty.to_string(ns),
-                self.expr_to_string(contract, ns, e)
-            ),
-            Expression::SMore(_, l, r) => format!(
-                "({} >(s) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::SLess(_, l, r) => format!(
-                "({} <(s) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::SMoreEqual(_, l, r) => format!(
-                "({} >=(s) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::SLessEqual(_, l, r) => format!(
-                "({} <=(s) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::UMore(_, l, r) => format!(
-                "({} >(u) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::ULess(_, l, r) => format!(
-                "({} <(u) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::UMoreEqual(_, l, r) => format!(
-                "({} >=(u) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::ULessEqual(_, l, r) => format!(
-                "({} <=(u) {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Equal(_, l, r) => format!(
-                "({} == {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::NotEqual(_, l, r) => format!(
-                "({} != {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::ArraySubscript(_, a, i) => format!(
-                "(array index {}[{}])",
-                self.expr_to_string(contract, ns, a),
-                self.expr_to_string(contract, ns, i)
-            ),
-            Expression::DynamicArraySubscript(_, a, _, i) => format!(
-                "(darray index {}[{}])",
-                self.expr_to_string(contract, ns, a),
-                self.expr_to_string(contract, ns, i)
-            ),
-            Expression::StorageBytesSubscript(_, a, i) => format!(
-                "(storage bytes index {}[{}])",
-                self.expr_to_string(contract, ns, a),
-                self.expr_to_string(contract, ns, i)
-            ),
-            Expression::StorageBytesPush(_, a, i) => format!(
-                "(storage bytes push {} {})",
-                self.expr_to_string(contract, ns, a),
-                self.expr_to_string(contract, ns, i)
-            ),
-            Expression::StorageBytesPop(_, a) => format!(
-                "(storage bytes pop {})",
-                self.expr_to_string(contract, ns, a),
-            ),
-            Expression::StorageBytesLength(_, a) => format!(
-                "(storage bytes length {})",
-                self.expr_to_string(contract, ns, a),
-            ),
-            Expression::StructMember(_, a, f) => format!(
-                "(struct {} field {})",
-                self.expr_to_string(contract, ns, a),
-                f
-            ),
-            Expression::Or(_, l, r) => format!(
-                "({} || {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::And(_, l, r) => format!(
-                "({} && {})",
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Ternary(_, c, l, r) => format!(
-                "({} ? {} : {})",
-                self.expr_to_string(contract, ns, c),
-                self.expr_to_string(contract, ns, l),
-                self.expr_to_string(contract, ns, r)
-            ),
-            Expression::Not(_, e) => format!("!{}", self.expr_to_string(contract, ns, e)),
-            Expression::Complement(_, e) => format!("~{}", self.expr_to_string(contract, ns, e)),
-            Expression::UnaryMinus(_, e) => format!("-{}", self.expr_to_string(contract, ns, e)),
-            Expression::Poison => "☠".to_string(),
-            Expression::Unreachable => "❌".to_string(),
-            Expression::AllocDynamicArray(_, ty, size, None) => format!(
-                "(alloc {} len {})",
-                ty.to_string(ns),
-                self.expr_to_string(contract, ns, size)
-            ),
-            Expression::AllocDynamicArray(_, ty, size, Some(init)) => format!(
-                "(alloc {} {} {})",
-                ty.to_string(ns),
-                self.expr_to_string(contract, ns, size),
-                match str::from_utf8(init) {
-                    Ok(s) => format!("\"{}\"", s.escape_debug()),
-                    Err(_) => format!("hex\"{}\"", hex::encode(init)),
-                }
-            ),
-            Expression::DynamicArrayLength(_, a) => {
-                format!("(array {} len)", self.expr_to_string(contract, ns, a))
-            }
-            Expression::StringCompare(_, l, r) => format!(
-                "(strcmp ({}) ({}))",
-                self.location_to_string(contract, ns, l),
-                self.location_to_string(contract, ns, r)
-            ),
-            Expression::StringConcat(_, l, r) => format!(
-                "(concat ({}) ({}))",
-                self.location_to_string(contract, ns, l),
-                self.location_to_string(contract, ns, r)
-            ),
-            Expression::Keccak256(_, exprs) => format!(
-                "(keccak256 {})",
-                exprs
-                    .iter()
-                    .map(|e| self.expr_to_string(contract, ns, &e.0))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::LocalFunctionCall(_, f, args) => format!(
-                "(call {} ({})",
-                contract.functions[*f].name,
-                args.iter()
-                    .map(|a| self.expr_to_string(contract, ns, &a))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::Constructor {
-                contract_no,
-                constructor_no,
-                args,
-                ..
-            } => format!(
-                "(constructor:{} ({}) ({})",
-                ns.contracts[*contract_no].name,
-                ns.contracts[*contract_no].functions[*constructor_no].signature,
-                args.iter()
-                    .map(|a| self.expr_to_string(contract, ns, &a))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::CodeLiteral(_, contract_no, runtime) => format!(
-                "({} code contract {})",
-                if *runtime {
-                    "runtimeCode"
-                } else {
-                    "creationCode"
-                },
-                ns.contracts[*contract_no].name,
-            ),
-            Expression::ExternalFunctionCall {
-                function_no,
-                contract_no,
-                address,
-                args,
-                ..
-            } => format!(
-                "(external call address:{} {}.{} ({})",
-                self.expr_to_string(contract, ns, address),
-                ns.contracts[*contract_no].name,
-                contract.functions[*function_no].name,
-                args.iter()
-                    .map(|a| self.expr_to_string(contract, ns, &a))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Expression::ReturnData(_) => "(external call return data)".to_string(),
-            Expression::GetAddress(_) => "(get adddress)".to_string(),
-            Expression::Balance(_, addr) => {
-                format!("(balance {})", self.expr_to_string(contract, ns, addr))
-            }
-        }
-    }
-
-    fn location_to_string(
-        &self,
-        contract: &resolver::Contract,
-        ns: &resolver::Namespace,
-        l: &StringLocation,
-    ) -> String {
-        match l {
-            StringLocation::RunTime(e) => self.expr_to_string(contract, ns, e),
-            StringLocation::CompileTime(literal) => match str::from_utf8(literal) {
-                Ok(s) => format!("\"{}\"", s.to_owned()),
-                Err(_) => format!("hex\"{}\"", hex::encode(literal)),
-            },
-        }
-    }
-
-    pub fn instr_to_string(
-        &self,
-        contract: &resolver::Contract,
-        ns: &resolver::Namespace,
-        instr: &Instr,
-    ) -> String {
-        match instr {
-            Instr::Return { value } => format!(
-                "return {}",
-                value
-                    .iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Instr::Set { res, expr } => format!(
-                "ty:{} %{} = {}",
-                self.vars[*res].ty.to_string(ns),
-                self.vars[*res].id.name,
-                self.expr_to_string(contract, ns, expr)
-            ),
-            Instr::Eval { expr } => format!("_ = {}", self.expr_to_string(contract, ns, expr)),
-            Instr::Constant { res, constant } => format!(
-                "%{} = const {}",
-                self.vars[*res].id.name,
-                self.expr_to_string(contract, ns, &contract.constants[*constant])
-            ),
-            Instr::Branch { bb } => format!("branch bb{}", bb),
-            Instr::BranchCond {
-                cond,
-                true_,
-                false_,
-            } => format!(
-                "branchcond {}, bb{}, bb{}",
-                self.expr_to_string(contract, ns, cond),
-                true_,
-                false_
-            ),
-            Instr::ClearStorage { ty, storage } => format!(
-                "clear storage slot({}) ty:{}",
-                self.expr_to_string(contract, ns, storage),
-                ty.to_string(ns),
-            ),
-            Instr::SetStorage { ty, local, storage } => format!(
-                "set storage slot({}) ty:{} = %{}",
-                self.expr_to_string(contract, ns, storage),
-                ty.to_string(ns),
-                self.vars[*local].id.name
-            ),
-            Instr::SetStorageBytes {
-                local,
-                storage,
-                offset,
-            } => format!(
-                "set storage slot({}) offset:{} = %{}",
-                self.expr_to_string(contract, ns, storage),
-                self.expr_to_string(contract, ns, offset),
-                self.vars[*local].id.name
-            ),
-            Instr::AssertFailure { expr: None } => "assert-failure".to_string(),
-            Instr::AssertFailure { expr: Some(expr) } => {
-                format!("assert-failure:{}", self.expr_to_string(contract, ns, expr))
-            }
-            Instr::Call { res, func, args } => format!(
-                "{} = call {} {} {}",
-                res.iter()
-                    .map(|local| format!("%{}", self.vars[*local].id.name))
-                    .collect::<Vec<String>>()
-                    .join(", "),
-                *func,
-                contract.functions[*func].name.to_owned(),
-                args.iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Instr::ExternalCall {
-                success,
-                address,
-                contract_no: Some(contract_no),
-                function_no,
-                args,
-                value,
-                gas,
-            } => format!(
-                "{} = external call address:{} signature:{} value:{} gas:{} func:{}.{} {}",
-                match success {
-                    Some(i) => format!("%{}", self.vars[*i].id.name),
-                    None => "_".to_string(),
-                },
-                self.expr_to_string(contract, ns, address),
-                ns.contracts[*contract_no].functions[*function_no].signature,
-                self.expr_to_string(contract, ns, value),
-                self.expr_to_string(contract, ns, gas),
-                ns.contracts[*contract_no].name,
-                ns.contracts[*contract_no].functions[*function_no].name,
-                args.iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Instr::ExternalCall {
-                success,
-                address,
-                contract_no: None,
-                value,
-                ..
-            } => format!(
-                "{} = external call address:{} value:{}",
-                match success {
-                    Some(i) => format!("%{}", self.vars[*i].id.name),
-                    None => "_".to_string(),
-                },
-                self.expr_to_string(contract, ns, address),
-                self.expr_to_string(contract, ns, value),
-            ),
-            Instr::AbiDecode {
-                res,
-                tys,
-                selector,
-                exception,
-                data,
-            } => format!(
-                "{} = (abidecode:(%{}, {} {} ({}))",
-                res.iter()
-                    .map(|local| format!("%{}", self.vars[*local].id.name))
-                    .collect::<Vec<String>>()
-                    .join(", "),
-                self.expr_to_string(contract, ns, data),
-                selector
-                    .iter()
-                    .map(|s| format!("selector:0x{:08x} ", s))
-                    .collect::<String>(),
-                exception
-                    .iter()
-                    .map(|bb| format!("exception:bb{} ", bb))
-                    .collect::<String>(),
-                tys.iter()
-                    .map(|ty| ty.ty.to_string(ns))
-                    .collect::<Vec<String>>()
-                    .join(", "),
-            ),
-            Instr::Store { dest, pos } => format!(
-                "store {}, {}",
-                self.expr_to_string(contract, ns, dest),
-                self.vars[*pos].id.name
-            ),
-            Instr::Print { expr } => format!("print {}", self.expr_to_string(contract, ns, expr)),
-            Instr::Constructor {
-                success,
-                res,
-                contract_no,
-                constructor_no,
-                args,
-                gas,
-                salt,
-                value,
-            } => format!(
-                "%{}, {} = constructor salt:{} value:{} gas:{} {} #{} ({})",
-                self.vars[*res].id.name,
-                match success {
-                    Some(i) => format!("%{}", self.vars[*i].id.name),
-                    None => "_".to_string(),
-                },
-                match salt {
-                    Some(salt) => self.expr_to_string(contract, ns, salt),
-                    None => "".to_string(),
-                },
-                match value {
-                    Some(value) => self.expr_to_string(contract, ns, value),
-                    None => "".to_string(),
-                },
-                self.expr_to_string(contract, ns, gas),
-                ns.contracts[*contract_no].name,
-                constructor_no,
-                args.iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
-            Instr::Unreachable => "unreachable".to_string(),
-            Instr::SelfDestruct { recipient } => format!(
-                "selfdestruct {}",
-                self.expr_to_string(contract, ns, recipient)
-            ),
-            Instr::Hash { res, hash, expr } => format!(
-                "%{} = hash {} {}",
-                self.vars[*res].id.name,
-                hash,
-                self.expr_to_string(contract, ns, expr)
-            ),
-        }
-    }
-
-    pub fn basic_block_to_string(
-        &self,
-        contract: &resolver::Contract,
-        ns: &resolver::Namespace,
-        pos: usize,
-    ) -> String {
-        let mut s = format!("bb{}: # {}\n", pos, self.bb[pos].name);
-
-        if let Some(ref phis) = self.bb[pos].phis {
-            s.push_str("# phis: ");
-            let mut first = true;
-            for p in phis {
-                if !first {
-                    s.push_str(", ");
-                }
-                first = false;
-                s.push_str(&self.vars[*p].id.name);
-            }
-            s.push_str("\n");
-        }
-
-        for ins in &self.bb[pos].instr {
-            s.push_str(&format!("\t{}\n", self.instr_to_string(contract, ns, ins)));
-        }
-
-        s
-    }
-
-    pub fn to_string(&self, contract: &resolver::Contract, ns: &resolver::Namespace) -> String {
-        let mut s = String::from("");
-
-        for i in 0..self.bb.len() {
-            s.push_str(&self.basic_block_to_string(contract, ns, i));
-        }
-
-        s
-    }
-}
-
-pub fn generate_cfg(
-    ast_f: &pt::FunctionDefinition,
-    resolve_f: &resolver::FunctionDecl,
-    contract_no: usize,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<Box<ControlFlowGraph>, ()> {
-    let mut cfg = Box::new(ControlFlowGraph::new());
-
-    let mut vartab = Vartable::new();
-    let mut loops = LoopScopes::new();
-
-    // first add function parameters
-    for (i, p) in ast_f.params.iter().enumerate() {
-        let p = p.1.as_ref().unwrap();
-        if let Some(ref name) = p.name {
-            if let Some(pos) = vartab.add(name, resolve_f.params[i].ty.clone(), errors) {
-                ns.check_shadowing(contract_no, name, errors);
-
-                cfg.add(
-                    &mut vartab,
-                    Instr::Set {
-                        res: pos,
-                        expr: Expression::FunctionArg(name.loc, i),
-                    },
-                );
-            }
-        }
-    }
-
-    // If any of the return values are named, then the return statement can be omitted at
-    // the end of the function, and return values may be omitted too. Create variables to
-    // store the return values
-    if ast_f
-        .returns
-        .iter()
-        .any(|v| v.1.as_ref().unwrap().name.is_some())
-    {
-        let mut returns = Vec::new();
-
-        for (i, p) in ast_f.returns.iter().enumerate() {
-            returns.push(if let Some(ref name) = p.1.as_ref().unwrap().name {
-                if let Some(pos) = vartab.add(name, resolve_f.returns[i].ty.clone(), errors) {
-                    ns.check_shadowing(contract_no, name, errors);
-
-                    // set to zero
-                    cfg.add(
-                        &mut vartab,
-                        Instr::Set {
-                            res: pos,
-                            expr: resolve_f.returns[i].ty.default(ns),
-                        },
-                    );
-
-                    pos
-                } else {
-                    // obs wrong but we had an error so will continue with bogus value to generate parser errors
-                    0
-                }
-            } else {
-                // this variable can never be assigned but will need a zero value
-                let pos = vartab.temp(
-                    &pt::Identifier {
-                        loc: pt::Loc(0, 0),
-                        name: format!("arg{}", i),
-                    },
-                    &resolve_f.returns[i].ty.clone(),
-                );
-
-                // set to zero
-                cfg.add(
-                    &mut vartab,
-                    Instr::Set {
-                        res: pos,
-                        expr: resolve_f.returns[i].ty.default(ns),
-                    },
-                );
-
-                pos
-            });
-        }
-
-        vartab.returns = returns;
-    }
-
-    let reachable = statement(
-        &ast_f.body,
-        resolve_f,
-        &mut cfg,
-        contract_no,
-        ns,
-        &mut vartab,
-        &mut loops,
-        errors,
-    )?;
-
-    // ensure we have a return instruction
-    if reachable {
-        check_return(ast_f, &mut cfg, &vartab, errors)?;
-    }
-
-    cfg.vars = vartab.drain();
-
-    // walk cfg to check for use for before initialize
-
-    Ok(cfg)
-}
-
-fn check_return(
-    f: &pt::FunctionDefinition,
-    cfg: &mut ControlFlowGraph,
-    vartab: &Vartable,
-    errors: &mut Vec<output::Output>,
-) -> Result<(), ()> {
-    let current = cfg.current;
-    let bb = &mut cfg.bb[current];
-
-    let num_instr = bb.instr.len();
-
-    if num_instr > 0 {
-        if let Instr::Return { .. } = bb.instr[num_instr - 1] {
-            return Ok(());
-        }
-    }
-
-    if f.returns.is_empty() || !vartab.returns.is_empty() {
-        bb.add(Instr::Return {
-            value: vartab
-                .returns
-                .iter()
-                .map(|pos| Expression::Variable(pt::Loc(0, 0), *pos))
-                .collect(),
-        });
-
-        Ok(())
-    } else {
-        errors.push(Output::error(
-            f.body.loc(),
-            "missing return statement".to_string(),
-        ));
-        Err(())
-    }
-}
-
-/// Resolve the type of a variable declaration
-pub fn resolve_var_decl_ty(
-    ty: &pt::Expression,
-    storage: &Option<pt::StorageLocation>,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<resolver::Type, ()> {
-    let mut var_ty = ns.resolve_type(contract_no, false, &ty, errors)?;
-
-    if let Some(storage) = storage {
-        if !var_ty.can_have_data_location() {
-            errors.push(Output::error(
-                *storage.loc(),
-                format!(
-                    "data location ‘{}’ only allowed for array, struct or mapping type",
-                    storage
-                ),
-            ));
-            return Err(());
-        }
-
-        if let pt::StorageLocation::Storage(_) = storage {
-            var_ty = resolver::Type::StorageRef(Box::new(var_ty));
-        }
-
-        // Note we are completely ignoring memory or calldata data locations. Everything
-        // will be stored in memory.
-    }
-
-    if var_ty.contains_mapping(ns) && !var_ty.is_contract_storage() {
-        errors.push(Output::error(
-            ty.loc(),
-            "mapping only allowed in storage".to_string(),
-        ));
-        return Err(());
-    }
-
-    if !var_ty.is_contract_storage() && var_ty.size_hint(ns) > BigInt::from(1024 * 1024) {
-        errors.push(Output::error(
-            ty.loc(),
-            "type to large to fit into memory".to_string(),
-        ));
-        return Err(());
-    }
-
-    Ok(var_ty)
-}
-
-/// Resolve a statement, which might be a block of statements or an entire body of a function
-fn statement(
-    stmt: &pt::Statement,
-    f: &resolver::FunctionDecl,
-    cfg: &mut ControlFlowGraph,
-    contract_no: usize,
-    ns: &resolver::Namespace,
-    vartab: &mut Vartable,
-    loops: &mut LoopScopes,
-    errors: &mut Vec<output::Output>,
-) -> Result<bool, ()> {
-    match stmt {
-        pt::Statement::VariableDefinition(_, decl, init) => {
-            let var_ty =
-                resolve_var_decl_ty(&decl.ty, &decl.storage, Some(contract_no), ns, errors)?;
-
-            let e_t = if let Some(init) = init {
-                let (expr, init_ty) =
-                    expression(init, cfg, Some(contract_no), ns, &mut Some(vartab), errors)?;
-
-                Some(cast(
-                    &decl.name.loc,
-                    expr,
-                    &init_ty,
-                    &var_ty,
-                    true,
-                    ns,
-                    errors,
-                )?)
-            } else {
-                None
-            };
-
-            if let Some(pos) = vartab.add(&decl.name, var_ty, errors) {
-                ns.check_shadowing(contract_no, &decl.name, errors);
-
-                if let Some(expr) = e_t {
-                    cfg.add(vartab, Instr::Set { res: pos, expr });
-                }
-            }
-            Ok(true)
-        }
-        pt::Statement::Block(_, bs) => {
-            vartab.new_scope();
-            let mut reachable = true;
-
-            for stmt in bs {
-                if !reachable {
-                    errors.push(Output::error(
-                        stmt.loc(),
-                        "unreachable statement".to_string(),
-                    ));
-                    return Err(());
-                }
-                reachable = statement(&stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-            }
-
-            vartab.leave_scope();
-
-            Ok(reachable)
-        }
-        pt::Statement::Return(loc, None) => {
-            let no_returns = f.returns.len();
-
-            if vartab.returns.len() != no_returns {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "missing return value, {} return values expected",
-                        no_returns
-                    ),
-                ));
-                return Err(());
-            }
-
-            cfg.add(
-                vartab,
-                Instr::Return {
-                    value: vartab
-                        .returns
-                        .iter()
-                        .map(|pos| Expression::Variable(pt::Loc(0, 0), *pos))
-                        .collect(),
-                },
-            );
-
-            Ok(false)
-        }
-        pt::Statement::Return(loc, Some(returns)) => {
-            return_with_values(returns, loc, f, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Statement::Expression(_, expr) => {
-            let (expr, _) =
-                expression(expr, cfg, Some(contract_no), ns, &mut Some(vartab), errors)?;
-
-            match expr {
-                Expression::Poison => {
-                    // ignore
-                    Ok(true)
-                }
-                Expression::Unreachable => {
-                    cfg.add(vartab, Instr::Unreachable);
-
-                    Ok(false)
-                }
-                _ => {
-                    cfg.add(vartab, Instr::Eval { expr });
-
-                    Ok(true)
-                }
-            }
-        }
-        pt::Statement::If(_, cond, then_stmt, None) => if_then(
-            cond,
-            then_stmt,
-            f,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            loops,
-            errors,
-        ),
-        pt::Statement::If(_, cond, then_stmt, Some(else_stmt)) => if_then_else(
-            cond,
-            then_stmt,
-            else_stmt,
-            f,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            loops,
-            errors,
-        ),
-        pt::Statement::Break(_) => match loops.do_break() {
-            Some(bb) => {
-                cfg.add(vartab, Instr::Branch { bb });
-                Ok(false)
-            }
-            None => {
-                errors.push(Output::error(
-                    stmt.loc(),
-                    "break statement not in loop".to_string(),
-                ));
-                Err(())
-            }
-        },
-        pt::Statement::Continue(_) => match loops.do_continue() {
-            Some(bb) => {
-                cfg.add(vartab, Instr::Branch { bb });
-                Ok(false)
-            }
-            None => {
-                errors.push(Output::error(
-                    stmt.loc(),
-                    "continue statement not in loop".to_string(),
-                ));
-                Err(())
-            }
-        },
-        pt::Statement::DoWhile(_, body_stmt, cond_expr) => {
-            let body = cfg.new_basic_block("body".to_string());
-            let cond = cfg.new_basic_block("conf".to_string());
-            let end = cfg.new_basic_block("enddowhile".to_string());
-
-            cfg.add(vartab, Instr::Branch { bb: body });
-
-            cfg.set_basic_block(body);
-
-            vartab.new_scope();
-            vartab.new_dirty_tracker();
-            loops.new_scope(end, cond);
-
-            let mut body_reachable =
-                statement(body_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-
-            if body_reachable {
-                cfg.add(vartab, Instr::Branch { bb: cond });
-            }
-
-            vartab.leave_scope();
-            let control = loops.leave_scope();
-
-            if control.no_continues > 0 {
-                body_reachable = true
-            }
-
-            if body_reachable {
-                cfg.set_basic_block(cond);
-
-                let (expr, expr_ty) = expression(
-                    cond_expr,
-                    cfg,
-                    Some(contract_no),
-                    ns,
-                    &mut Some(vartab),
-                    errors,
-                )?;
-
-                cfg.add(
-                    vartab,
-                    Instr::BranchCond {
-                        cond: cast(
-                            &cond_expr.loc(),
-                            expr,
-                            &expr_ty,
-                            &resolver::Type::Bool,
-                            true,
-                            ns,
-                            errors,
-                        )?,
-                        true_: body,
-                        false_: end,
-                    },
-                );
-            }
-
-            let set = vartab.pop_dirty_tracker();
-            cfg.set_phis(end, set.clone());
-            cfg.set_phis(body, set.clone());
-            cfg.set_phis(cond, set);
-
-            cfg.set_basic_block(end);
-
-            Ok(body_reachable || control.no_breaks > 0)
-        }
-        pt::Statement::While(_, cond_expr, body_stmt) => {
-            let cond = cfg.new_basic_block("cond".to_string());
-            let body = cfg.new_basic_block("body".to_string());
-            let end = cfg.new_basic_block("endwhile".to_string());
-
-            cfg.add(vartab, Instr::Branch { bb: cond });
-
-            cfg.set_basic_block(cond);
-
-            let (expr, expr_ty) = expression(
-                cond_expr,
-                cfg,
-                Some(contract_no),
-                ns,
-                &mut Some(vartab),
-                errors,
-            )?;
-
-            cfg.add(
-                vartab,
-                Instr::BranchCond {
-                    cond: cast(
-                        &cond_expr.loc(),
-                        expr,
-                        &expr_ty,
-                        &resolver::Type::Bool,
-                        true,
-                        ns,
-                        errors,
-                    )?,
-                    true_: body,
-                    false_: end,
-                },
-            );
-
-            cfg.set_basic_block(body);
-
-            vartab.new_scope();
-            vartab.new_dirty_tracker();
-            loops.new_scope(end, cond);
-
-            let body_reachable =
-                statement(body_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-
-            if body_reachable {
-                cfg.add(vartab, Instr::Branch { bb: cond });
-            }
-
-            vartab.leave_scope();
-            loops.leave_scope();
-            let set = vartab.pop_dirty_tracker();
-            cfg.set_phis(end, set.clone());
-            cfg.set_phis(cond, set);
-
-            cfg.set_basic_block(end);
-
-            Ok(true)
-        }
-        pt::Statement::For(_, init_stmt, None, next_stmt, body_stmt) => {
-            let body = cfg.new_basic_block("body".to_string());
-            let next = cfg.new_basic_block("next".to_string());
-            let end = cfg.new_basic_block("endfor".to_string());
-
-            vartab.new_scope();
-
-            if let Some(init_stmt) = init_stmt {
-                statement(init_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-            }
-
-            cfg.add(vartab, Instr::Branch { bb: body });
-
-            cfg.set_basic_block(body);
-
-            loops.new_scope(
-                end,
-                match next_stmt {
-                    Some(_) => next,
-                    None => body,
-                },
-            );
-            vartab.new_dirty_tracker();
-
-            let mut body_reachable = match body_stmt {
-                Some(body_stmt) => {
-                    statement(body_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?
-                }
-                None => true,
-            };
-
-            if body_reachable {
-                cfg.add(vartab, Instr::Branch { bb: next });
-            }
-
-            let control = loops.leave_scope();
-
-            if control.no_continues > 0 {
-                body_reachable = true;
-            }
-
-            if body_reachable {
-                if let Some(next_stmt) = next_stmt {
-                    cfg.set_basic_block(next);
-                    body_reachable =
-                        statement(next_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-                }
-
-                if body_reachable {
-                    cfg.add(vartab, Instr::Branch { bb: body });
-                }
-            }
-
-            let set = vartab.pop_dirty_tracker();
-            if control.no_continues > 0 {
-                cfg.set_phis(next, set.clone());
-            }
-            cfg.set_phis(body, set.clone());
-            cfg.set_phis(end, set);
-
-            vartab.leave_scope();
-            cfg.set_basic_block(end);
-
-            Ok(control.no_breaks > 0)
-        }
-        pt::Statement::For(_, init_stmt, Some(cond_expr), next_stmt, body_stmt) => {
-            let body = cfg.new_basic_block("body".to_string());
-            let cond = cfg.new_basic_block("cond".to_string());
-            let next = cfg.new_basic_block("next".to_string());
-            let end = cfg.new_basic_block("endfor".to_string());
-
-            vartab.new_scope();
-
-            if let Some(init_stmt) = init_stmt {
-                statement(init_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-            }
-
-            cfg.add(vartab, Instr::Branch { bb: cond });
-
-            cfg.set_basic_block(cond);
-
-            let (expr, expr_ty) = expression(
-                cond_expr,
-                cfg,
-                Some(contract_no),
-                ns,
-                &mut Some(vartab),
-                errors,
-            )?;
-
-            cfg.add(
-                vartab,
-                Instr::BranchCond {
-                    cond: cast(
-                        &cond_expr.loc(),
-                        expr,
-                        &expr_ty,
-                        &resolver::Type::Bool,
-                        true,
-                        ns,
-                        errors,
-                    )?,
-                    true_: body,
-                    false_: end,
-                },
-            );
-
-            cfg.set_basic_block(body);
-
-            // continue goes to next, and if that does exist, cond
-            loops.new_scope(
-                end,
-                match next_stmt {
-                    Some(_) => next,
-                    None => cond,
-                },
-            );
-            vartab.new_dirty_tracker();
-
-            let mut body_reachable = match body_stmt {
-                Some(body_stmt) => {
-                    statement(body_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?
-                }
-                None => true,
-            };
-
-            if body_reachable {
-                cfg.add(vartab, Instr::Branch { bb: next });
-            }
-
-            let control = loops.leave_scope();
-
-            if control.no_continues > 0 {
-                body_reachable = true;
-            }
-
-            if body_reachable {
-                cfg.set_basic_block(next);
-
-                if let Some(next_stmt) = next_stmt {
-                    body_reachable =
-                        statement(next_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-                }
-
-                if body_reachable {
-                    cfg.add(vartab, Instr::Branch { bb: cond });
-                }
-            }
-
-            vartab.leave_scope();
-            cfg.set_basic_block(end);
-
-            let set = vartab.pop_dirty_tracker();
-            if control.no_continues > 0 {
-                cfg.set_phis(next, set.clone());
-            }
-            if control.no_breaks > 0 {
-                cfg.set_phis(end, set.clone());
-            }
-            cfg.set_phis(cond, set);
-
-            Ok(true)
-        }
-        pt::Statement::Try(_, _, _, _, _) => {
-            try_catch(stmt, f, cfg, contract_no, ns, vartab, loops, errors)
-        }
-        pt::Statement::Args(_, _) => {
-            errors.push(Output::error(
-                stmt.loc(),
-                "expected code block, not list of named arguments".to_string(),
-            ));
-            Err(())
-        }
-        _ => panic!("not implemented"),
-    }
-}
-
-/// Parse return statement with values
-fn return_with_values(
-    returns: &pt::Expression,
-    loc: &pt::Loc,
-    f: &resolver::FunctionDecl,
-    cfg: &mut ControlFlowGraph,
-    contract_no: usize,
-    ns: &resolver::Namespace,
-    vartab: &mut Vartable,
-    errors: &mut Vec<output::Output>,
-) -> Result<bool, ()> {
-    let returns = parameter_list_to_expr_list(returns, errors)?;
-
-    let no_returns = f.returns.len();
-
-    if no_returns > 0 && returns.is_empty() {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "missing return value, {} return values expected",
-                no_returns
-            ),
-        ));
-        return Err(());
-    }
-
-    if no_returns == 0 && !returns.is_empty() {
-        errors.push(Output::error(
-            *loc,
-            "function has no return values".to_string(),
-        ));
-        return Err(());
-    }
-
-    if no_returns != returns.len() {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "incorrect number of return values, expected {} but got {}",
-                no_returns,
-                returns.len()
-            ),
-        ));
-        return Err(());
-    }
-
-    let mut exprs = Vec::new();
-
-    for (i, r) in returns.iter().enumerate() {
-        let (e, ty) = expression(r, cfg, Some(contract_no), ns, &mut Some(vartab), errors)?;
-
-        exprs.push(cast(&r.loc(), e, &ty, &f.returns[i].ty, true, ns, errors)?);
-    }
-
-    cfg.add(vartab, Instr::Return { value: exprs });
-
-    Ok(false)
-}
-
-/// Parse if-then-no-else
-fn if_then(
-    cond: &pt::Expression,
-    then_stmt: &pt::Statement,
-    f: &resolver::FunctionDecl,
-    cfg: &mut ControlFlowGraph,
-    contract_no: usize,
-    ns: &resolver::Namespace,
-    vartab: &mut Vartable,
-    loops: &mut LoopScopes,
-    errors: &mut Vec<output::Output>,
-) -> Result<bool, ()> {
-    let (expr, expr_ty) = expression(cond, cfg, Some(contract_no), ns, &mut Some(vartab), errors)?;
-
-    let then = cfg.new_basic_block("then".to_string());
-    let endif = cfg.new_basic_block("endif".to_string());
-
-    cfg.add(
-        vartab,
-        Instr::BranchCond {
-            cond: cast(
-                &cond.loc(),
-                expr,
-                &expr_ty,
-                &resolver::Type::Bool,
-                true,
-                ns,
-                errors,
-            )?,
-            true_: then,
-            false_: endif,
-        },
-    );
-
-    cfg.set_basic_block(then);
-
-    vartab.new_scope();
-    vartab.new_dirty_tracker();
-
-    let reachable = statement(then_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-
-    if reachable {
-        cfg.add(vartab, Instr::Branch { bb: endif });
-    }
-
-    vartab.leave_scope();
-    cfg.set_phis(endif, vartab.pop_dirty_tracker());
-
-    cfg.set_basic_block(endif);
-
-    Ok(true)
-}
-
-/// Parse if-then-else
-fn if_then_else(
-    cond: &pt::Expression,
-    then_stmt: &pt::Statement,
-    else_stmt: &pt::Statement,
-    f: &resolver::FunctionDecl,
-    cfg: &mut ControlFlowGraph,
-    contract_no: usize,
-    ns: &resolver::Namespace,
-    vartab: &mut Vartable,
-    loops: &mut LoopScopes,
-    errors: &mut Vec<output::Output>,
-) -> Result<bool, ()> {
-    let (expr, expr_ty) = expression(cond, cfg, Some(contract_no), ns, &mut Some(vartab), errors)?;
-
-    let then = cfg.new_basic_block("then".to_string());
-    let else_ = cfg.new_basic_block("else".to_string());
-    let endif = cfg.new_basic_block("endif".to_string());
-
-    cfg.add(
-        vartab,
-        Instr::BranchCond {
-            cond: cast(
-                &cond.loc(),
-                expr,
-                &expr_ty,
-                &resolver::Type::Bool,
-                true,
-                ns,
-                errors,
-            )?,
-            true_: then,
-            false_: else_,
-        },
-    );
-
-    // then
-    cfg.set_basic_block(then);
-
-    vartab.new_scope();
-    vartab.new_dirty_tracker();
-
-    let then_reachable = statement(then_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-
-    if then_reachable {
-        cfg.add(vartab, Instr::Branch { bb: endif });
-    }
-
-    vartab.leave_scope();
-
-    // else
-    cfg.set_basic_block(else_);
-
-    vartab.new_scope();
-
-    let else_reachable = statement(else_stmt, f, cfg, contract_no, ns, vartab, loops, errors)?;
-
-    if else_reachable {
-        cfg.add(vartab, Instr::Branch { bb: endif });
-    }
-
-    vartab.leave_scope();
-    cfg.set_phis(endif, vartab.pop_dirty_tracker());
-
-    cfg.set_basic_block(endif);
-
-    Ok(then_reachable || else_reachable)
-}
-
-/// Resolve try catch statement
-fn try_catch(
-    try: &pt::Statement,
-    f: &resolver::FunctionDecl,
-    cfg: &mut ControlFlowGraph,
-    contract_no: usize,
-    ns: &resolver::Namespace,
-    vartab: &mut Vartable,
-    loops: &mut LoopScopes,
-    errors: &mut Vec<output::Output>,
-) -> Result<bool, ()> {
-    if let pt::Statement::Try(_, expr, returns_and_ok, error_stmt, catch_stmt) = &try {
-        let mut expr = expr;
-        let mut ok = None;
-
-        while let pt::Expression::FunctionCallBlock(_, e, block) = expr {
-            if ok.is_some() {
-                errors.push(Output::error(
-                    block.loc(),
-                    "unexpected code block".to_string(),
-                ));
-                return Err(());
-            }
-
-            ok = Some(block.as_ref());
-
-            expr = e.as_ref();
-        }
-
-        let fcall = match expr {
-            pt::Expression::FunctionCall(loc, ty, args) => function_call_expr(
-                loc,
-                ty,
-                args,
-                cfg,
-                Some(contract_no),
-                ns,
-                &mut Some(vartab),
-                errors,
-            )?,
-            pt::Expression::NamedFunctionCall(loc, ty, args) => named_function_call_expr(
-                loc,
-                ty,
-                args,
-                cfg,
-                Some(contract_no),
-                ns,
-                &mut Some(vartab),
-                errors,
-            )?,
-            pt::Expression::New(loc, call) => {
-                let mut call = call.as_ref();
-
-                while let pt::Expression::FunctionCallBlock(_, expr, block) = call {
-                    if ok.is_some() {
-                        errors.push(Output::error(
-                            block.loc(),
-                            "unexpected code block".to_string(),
-                        ));
-                        return Err(());
-                    }
-
-                    ok = Some(block.as_ref());
-
-                    call = expr.as_ref();
-                }
-
-                match call {
-                    pt::Expression::FunctionCall(_, ty, args) => new(
-                        loc,
-                        ty,
-                        args,
-                        cfg,
-                        Some(contract_no),
-                        ns,
-                        &mut Some(vartab),
-                        errors,
-                    )?,
-                    pt::Expression::NamedFunctionCall(_, ty, args) => constructor_named_args(
-                        loc,
-                        ty,
-                        args,
-                        cfg,
-                        Some(contract_no),
-                        ns,
-                        &mut Some(vartab),
-                        errors,
-                    )?,
-                    _ => unreachable!(),
-                }
-            }
-            _ => {
-                errors.push(Output::error(
-                    expr.loc(),
-                    "try only supports external calls or constructor calls".to_string(),
-                ));
-                return Err(());
-            }
-        };
-
-        let mut returns = &Vec::new();
-
-        if let Some((rets, block)) = returns_and_ok {
-            if ok.is_some() {
-                errors.push(Output::error(
-                    block.loc(),
-                    "unexpected code block".to_string(),
-                ));
-                return Err(());
-            }
-
-            ok = Some(block);
-
-            returns = rets;
-        }
-
-        let ok = match ok {
-            Some(ok) => ok,
-            None => {
-                // position after the expression
-                let pos = expr.loc().1;
-
-                errors.push(Output::error(
-                    pt::Loc(pos, pos),
-                    "code block missing for no catch".to_string(),
-                ));
-                return Err(());
-            }
-        };
-
-        let success = vartab.temp(
-            &pt::Identifier {
-                loc: pt::Loc(0, 0),
-                name: "success".to_owned(),
-            },
-            &resolver::Type::Bool,
-        );
-
-        let success_block = cfg.new_basic_block("success".to_string());
-        let catch_block = cfg.new_basic_block("catch".to_string());
-        let finally_block = cfg.new_basic_block("finally".to_string());
-
-        let mut args = match fcall.0 {
-            Expression::ExternalFunctionCall {
-                contract_no,
-                function_no,
-                address,
-                args,
-                value,
-                gas,
-                ..
-            } => {
-                cfg.add(
-                    vartab,
-                    Instr::ExternalCall {
-                        success: Some(success),
-                        address: *address,
-                        contract_no: Some(contract_no),
-                        function_no,
-                        args,
-                        value: *value,
-                        gas: *gas,
-                    },
-                );
-
-                let ftype = &ns.contracts[contract_no].functions[function_no];
-
-                cfg.add(
-                    vartab,
-                    Instr::BranchCond {
-                        cond: Expression::Variable(pt::Loc(0, 0), success),
-                        true_: success_block,
-                        false_: catch_block,
-                    },
-                );
-
-                cfg.set_basic_block(success_block);
-
-                if returns.len() != ftype.returns.len() {
-                    errors.push(Output::error(
-                        expr.loc(),
-                        format!(
-                            "try returns list has {} entries while function returns {} values",
-                            ftype.returns.len(),
-                            returns.len()
-                        ),
-                    ));
-                    return Err(());
-                }
-
-                if !ftype.returns.is_empty() {
-                    let mut returns = Vec::new();
-                    let mut res = Vec::new();
-                    for ret in &ftype.returns {
-                        let id = pt::Identifier {
-                            loc: pt::Loc(0, 0),
-                            name: "".to_owned(),
-                        };
-                        let temp_pos = vartab.temp(&id, &ret.ty);
-                        res.push(temp_pos);
-                        returns.push((Expression::Variable(id.loc, temp_pos), ret.ty.clone()));
-                    }
-                    cfg.add(
-                        vartab,
-                        Instr::AbiDecode {
-                            res,
-                            selector: None,
-                            exception: None,
-                            tys: ftype.returns.clone(),
-                            data: Expression::ReturnData(pt::Loc(0, 0)),
-                        },
-                    );
-                    returns
-                } else {
-                    Vec::new()
-                }
-            }
-            Expression::Constructor {
-                loc,
-                contract_no,
-                constructor_no,
-                args,
-                value,
-                gas,
-                salt,
-            } => {
-                let ty = resolver::Type::Contract(contract_no);
-                let address_res = vartab.temp_anonymous(&resolver::Type::Contract(contract_no));
-
-                cfg.add(
-                    vartab,
-                    Instr::Constructor {
-                        success: Some(success),
-                        res: address_res,
-                        contract_no,
-                        constructor_no,
-                        args,
-                        value: value.map(|v| *v),
-                        gas: *gas,
-                        salt: salt.map(|v| *v),
-                    },
-                );
-
-                cfg.add(
-                    vartab,
-                    Instr::BranchCond {
-                        cond: Expression::Variable(pt::Loc(0, 0), success),
-                        true_: success_block,
-                        false_: catch_block,
-                    },
-                );
-
-                cfg.set_basic_block(success_block);
-
-                match returns.len() {
-                    0 => Vec::new(),
-                    1 => vec![(Expression::Variable(loc, address_res), ty)],
-                    _ => {
-                        errors.push(Output::error(
-                            expr.loc(),
-                            format!(
-                                "constructor returns single contract, not {} values",
-                                returns.len()
-                            ),
-                        ));
-                        return Err(());
-                    }
-                }
-            }
-            _ => {
-                errors.push(Output::error(
-                    expr.loc(),
-                    "try only supports external calls or constructor calls".to_string(),
-                ));
-                return Err(());
-            }
-        };
-
-        vartab.new_scope();
-        vartab.new_dirty_tracker();
-
-        let mut broken = false;
-        for param in returns.iter() {
-            let (arg, arg_ty) = args.remove(0);
-
-            match &param.1 {
-                Some(pt::Parameter { ty, storage, name }) => {
-                    let ret_ty = resolve_var_decl_ty(&ty, &storage, Some(contract_no), ns, errors)?;
-
-                    if arg_ty != ret_ty {
-                        errors.push(Output::error(
-                            ty.loc(),
-                            format!(
-                                "type ‘{}’ does not match return value of function ‘{}’",
-                                ret_ty.to_string(ns),
-                                arg_ty.to_string(ns)
-                            ),
-                        ));
-                        broken = true;
-                    }
-
-                    if let Some(name) = name {
-                        if let Some(pos) = vartab.add(&name, ret_ty, errors) {
-                            ns.check_shadowing(contract_no, &name, errors);
-
-                            cfg.add(
-                                vartab,
-                                Instr::Set {
-                                    res: pos,
-                                    expr: arg,
-                                },
-                            );
-                        }
-                    }
-                }
-                None => (),
-            }
-        }
-
-        if broken {
-            return Err(());
-        }
-
-        let mut finally_reachable = statement(&ok, f, cfg, contract_no, ns, vartab, loops, errors)?;
-
-        if finally_reachable {
-            cfg.add(vartab, Instr::Branch { bb: finally_block });
-        }
-
-        vartab.leave_scope();
-
-        cfg.set_basic_block(catch_block);
-
-        if let Some(error_stmt) = error_stmt {
-            if error_stmt.0.name != "Error" {
-                errors.push(Output::error(
-                    error_stmt.0.loc,
-                    format!(
-                        "only catch ‘Error’ is supported, not ‘{}’",
-                        error_stmt.0.name
-                    ),
-                ));
-                return Err(());
-            }
-
-            let no_reason_block = cfg.new_basic_block("no_reason".to_string());
-
-            let error_ty = resolve_var_decl_ty(
-                &error_stmt.1.ty,
-                &error_stmt.1.storage,
-                Some(contract_no),
-                ns,
-                errors,
-            )?;
-
-            if error_ty != resolver::Type::String {
-                errors.push(Output::error(
-                    error_stmt.1.ty.loc(),
-                    format!(
-                        "catch Error(...) can only take ‘string memory’, not ‘{}’",
-                        error_ty.to_string(ns)
-                    ),
-                ));
-            }
-
-            let error_var = vartab.temp_anonymous(&resolver::Type::String);
-
-            cfg.add(
-                vartab,
-                Instr::AbiDecode {
-                    selector: Some(0x08c3_79a0),
-                    exception: Some(no_reason_block),
-                    res: vec![error_var],
-                    tys: vec![resolver::Parameter {
-                        name: "error".to_string(),
-                        ty: resolver::Type::String,
-                    }],
-                    data: Expression::ReturnData(pt::Loc(0, 0)),
-                },
-            );
-
-            vartab.new_scope();
-
-            if let Some(name) = &error_stmt.1.name {
-                if let Some(pos) = vartab.add(&name, resolver::Type::String, errors) {
-                    ns.check_shadowing(contract_no, &name, errors);
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: pos,
-                            expr: Expression::Variable(pt::Loc(0, 0), error_var),
-                        },
-                    );
-                }
-            }
-
-            let reachable = statement(
-                &error_stmt.2,
-                f,
-                cfg,
-                contract_no,
-                ns,
-                vartab,
-                loops,
-                errors,
-            )?;
-
-            if reachable {
-                cfg.add(vartab, Instr::Branch { bb: finally_block });
-            }
-
-            finally_reachable &= reachable;
-
-            vartab.leave_scope();
-
-            cfg.set_basic_block(no_reason_block);
-        }
-
-        let catch_ty = resolve_var_decl_ty(
-            &catch_stmt.0.ty,
-            &catch_stmt.0.storage,
-            Some(contract_no),
-            ns,
-            errors,
-        )?;
-
-        if catch_ty != resolver::Type::DynamicBytes {
-            errors.push(Output::error(
-                catch_stmt.0.ty.loc(),
-                format!(
-                    "catch can only take ‘bytes memory’, not ‘{}’",
-                    catch_ty.to_string(ns)
-                ),
-            ));
-            return Err(());
-        }
-
-        vartab.new_scope();
-
-        if let Some(name) = &catch_stmt.0.name {
-            if let Some(pos) = vartab.add(&name, catch_ty, errors) {
-                ns.check_shadowing(contract_no, &name, errors);
-
-                cfg.add(
-                    vartab,
-                    Instr::Set {
-                        res: pos,
-                        expr: Expression::ReturnData(pt::Loc(0, 0)),
-                    },
-                );
-            }
-        }
-
-        let reachable = statement(
-            &catch_stmt.1,
-            f,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            loops,
-            errors,
-        )?;
-
-        if reachable {
-            cfg.add(vartab, Instr::Branch { bb: finally_block });
-        }
-
-        finally_reachable &= reachable;
-
-        vartab.leave_scope();
-
-        let set = vartab.pop_dirty_tracker();
-        cfg.set_phis(finally_block, set);
-
-        cfg.set_basic_block(finally_block);
-
-        Ok(finally_reachable)
-    } else {
-        unreachable!()
-    }
-}
-
-// Vartable
-// methods
-// create variable with loc, name, Type -> pos
-// find variable by name -> Some(pos)
-// new scope
-// leave scope
-// produce full Vector of all variables
-#[derive(Clone)]
-pub enum Storage {
-    Constant(usize),
-    Contract(BigInt),
-    Local,
-}
-
-#[derive(Clone)]
-pub struct Variable {
-    pub id: pt::Identifier,
-    pub ty: resolver::Type,
-    pub pos: usize,
-    pub storage: Storage,
-}
-
-struct VarScope(HashMap<String, usize>, Option<HashSet<usize>>);
-
-#[derive(Default)]
-pub struct Vartable {
-    vars: Vec<Variable>,
-    names: LinkedList<VarScope>,
-    storage_vars: HashMap<String, usize>,
-    dirty: Vec<DirtyTracker>,
-    returns: Vec<usize>,
-}
-
-pub struct DirtyTracker {
-    lim: usize,
-    set: HashSet<usize>,
-}
-
-impl Vartable {
-    pub fn new() -> Self {
-        let mut list = LinkedList::new();
-        list.push_front(VarScope(HashMap::new(), None));
-        Vartable {
-            vars: Vec::new(),
-            names: list,
-            storage_vars: HashMap::new(),
-            dirty: Vec::new(),
-            returns: Vec::new(),
-        }
-    }
-
-    pub fn add(
-        &mut self,
-        id: &pt::Identifier,
-        ty: resolver::Type,
-        errors: &mut Vec<output::Output>,
-    ) -> Option<usize> {
-        if let Some(ref prev) = self.find_local(&id.name) {
-            errors.push(Output::error_with_note(
-                id.loc,
-                format!("{} is already declared", id.name.to_string()),
-                prev.id.loc,
-                "location of previous declaration".to_string(),
-            ));
-            return None;
-        }
-
-        let pos = self.vars.len();
-
-        self.vars.push(Variable {
-            id: id.clone(),
-            ty,
-            pos,
-            storage: Storage::Local,
-        });
-
-        self.names
-            .front_mut()
-            .unwrap()
-            .0
-            .insert(id.name.to_string(), pos);
-
-        Some(pos)
-    }
-
-    fn find_local(&self, name: &str) -> Option<&Variable> {
-        for scope in &self.names {
-            if let Some(n) = scope.0.get(name) {
-                return Some(&self.vars[*n]);
-            }
-        }
-
-        None
-    }
-
-    pub fn find(
-        &mut self,
-        id: &pt::Identifier,
-        contract_no: usize,
-        ns: &resolver::Namespace,
-        errors: &mut Vec<output::Output>,
-    ) -> Result<Variable, ()> {
-        for scope in &self.names {
-            if let Some(n) = scope.0.get(&id.name) {
-                return Ok(self.vars[*n].clone());
-            }
-        }
-
-        if let Some(n) = self.storage_vars.get(&id.name) {
-            return Ok(self.vars[*n].clone());
-        }
-
-        let v = ns.resolve_var(contract_no, &id, errors)?;
-        let var = &ns.contracts[contract_no].variables[v];
-        let pos = self.vars.len();
-
-        self.vars.push(Variable {
-            id: id.clone(),
-            ty: var.ty.clone(),
-            pos,
-            storage: match &var.var {
-                resolver::ContractVariableType::Storage(n) => Storage::Contract(n.clone()),
-                resolver::ContractVariableType::Constant(n) => Storage::Constant(*n),
-            },
-        });
-
-        self.storage_vars.insert(id.name.to_string(), pos);
-
-        Ok(self.vars[pos].clone())
-    }
-
-    pub fn temp_anonymous(&mut self, ty: &resolver::Type) -> usize {
-        let pos = self.vars.len();
-
-        self.vars.push(Variable {
-            id: pt::Identifier {
-                name: format!("temp.{}", pos),
-                loc: pt::Loc(0, 0),
-            },
-            ty: ty.clone(),
-            pos,
-            storage: Storage::Local,
-        });
-
-        pos
-    }
-
-    pub fn temp(&mut self, id: &pt::Identifier, ty: &resolver::Type) -> usize {
-        let pos = self.vars.len();
-
-        self.vars.push(Variable {
-            id: pt::Identifier {
-                name: format!("{}.temp.{}", id.name, pos),
-                loc: id.loc,
-            },
-            ty: ty.clone(),
-            pos,
-            storage: Storage::Local,
-        });
-
-        pos
-    }
-
-    pub fn new_scope(&mut self) {
-        self.names.push_front(VarScope(HashMap::new(), None));
-    }
-
-    pub fn leave_scope(&mut self) {
-        self.names.pop_front();
-    }
-
-    pub fn drain(self) -> Vec<Variable> {
-        self.vars
-    }
-
-    // In order to create phi nodes, we need to track what vars are set in a certain scope
-    pub fn set_dirty(&mut self, pos: usize) {
-        for e in &mut self.dirty {
-            if pos < e.lim {
-                e.set.insert(pos);
-            }
-        }
-    }
-
-    pub fn new_dirty_tracker(&mut self) {
-        self.dirty.push(DirtyTracker {
-            lim: self.vars.len(),
-            set: HashSet::new(),
-        });
-    }
-
-    pub fn pop_dirty_tracker(&mut self) -> HashSet<usize> {
-        self.dirty.pop().unwrap().set
-    }
-}
-
-struct LoopScope {
-    break_bb: usize,
-    continue_bb: usize,
-    no_breaks: usize,
-    no_continues: usize,
-}
-
-struct LoopScopes(LinkedList<LoopScope>);
-
-impl LoopScopes {
-    fn new() -> Self {
-        LoopScopes(LinkedList::new())
-    }
-
-    fn new_scope(&mut self, break_bb: usize, continue_bb: usize) {
-        self.0.push_front(LoopScope {
-            break_bb,
-            continue_bb,
-            no_breaks: 0,
-            no_continues: 0,
-        })
-    }
-
-    fn leave_scope(&mut self) -> LoopScope {
-        self.0.pop_front().unwrap()
-    }
-
-    fn do_break(&mut self) -> Option<usize> {
-        match self.0.front_mut() {
-            Some(scope) => {
-                scope.no_breaks += 1;
-                Some(scope.break_bb)
-            }
-            None => None,
-        }
-    }
-
-    fn do_continue(&mut self) -> Option<usize> {
-        match self.0.front_mut() {
-            Some(scope) => {
-                scope.no_continues += 1;
-                Some(scope.continue_bb)
-            }
-            None => None,
-        }
-    }
-}
-
-impl resolver::Type {
-    fn default(&self, ns: &resolver::Namespace) -> Expression {
-        match self {
-            resolver::Type::Uint(b) | resolver::Type::Int(b) => {
-                Expression::NumberLiteral(pt::Loc(0, 0), *b, BigInt::from(0))
-            }
-            resolver::Type::Bool => Expression::BoolLiteral(pt::Loc(0, 0), false),
-            resolver::Type::Address(_) => Expression::NumberLiteral(
-                pt::Loc(0, 0),
-                ns.address_length as u16 * 8,
-                BigInt::from(0),
-            ),
-            resolver::Type::Bytes(n) => {
-                let mut l = Vec::new();
-                l.resize(*n as usize, 0);
-                Expression::BytesLiteral(pt::Loc(0, 0), l)
-            }
-            resolver::Type::Enum(e) => ns.enums[*e].ty.default(ns),
-            resolver::Type::Struct(_) => {
-                Expression::StructLiteral(pt::Loc(0, 0), self.clone(), Vec::new())
-            }
-            resolver::Type::Ref(_) => unreachable!(),
-            resolver::Type::StorageRef(_) => unreachable!(),
-            _ => unreachable!(),
-        }
-    }
-}

+ 0 - 133
src/resolver/eval.rs

@@ -1,133 +0,0 @@
-use num_bigint::BigInt;
-use num_bigint::Sign;
-use num_traits::One;
-use num_traits::ToPrimitive;
-use num_traits::Zero;
-
-use super::expression::Expression;
-use output;
-use output::Output;
-use parser::pt::Loc;
-
-/// Resolve an expression where a compile-time constant is expected
-pub fn eval_number_expression(
-    expr: &Expression,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Loc, BigInt), ()> {
-    match expr {
-        Expression::Add(loc, l, r) => Ok((
-            *loc,
-            eval_number_expression(l, errors)?.1 + eval_number_expression(r, errors)?.1,
-        )),
-        Expression::Subtract(loc, l, r) => Ok((
-            *loc,
-            eval_number_expression(l, errors)?.1 - eval_number_expression(r, errors)?.1,
-        )),
-        Expression::Multiply(loc, l, r) => Ok((
-            *loc,
-            eval_number_expression(l, errors)?.1 * eval_number_expression(r, errors)?.1,
-        )),
-        Expression::UDivide(loc, l, r) | Expression::SDivide(loc, l, r) => {
-            let divisor = eval_number_expression(r, errors)?.1;
-
-            if divisor.is_zero() {
-                errors.push(Output::error(*loc, "divide by zero".to_string()));
-
-                Err(())
-            } else {
-                Ok((*loc, eval_number_expression(l, errors)?.1 / divisor))
-            }
-        }
-        Expression::UModulo(loc, l, r) | Expression::SModulo(loc, l, r) => {
-            let divisor = eval_number_expression(r, errors)?.1;
-
-            if divisor.is_zero() {
-                errors.push(Output::error(*loc, "divide by zero".to_string()));
-
-                Err(())
-            } else {
-                Ok((*loc, eval_number_expression(l, errors)?.1 % divisor))
-            }
-        }
-        Expression::BitwiseAnd(loc, l, r) => Ok((
-            *loc,
-            eval_number_expression(l, errors)?.1 & eval_number_expression(r, errors)?.1,
-        )),
-        Expression::BitwiseOr(loc, l, r) => Ok((
-            *loc,
-            eval_number_expression(l, errors)?.1 | eval_number_expression(r, errors)?.1,
-        )),
-        Expression::BitwiseXor(loc, l, r) => Ok((
-            *loc,
-            eval_number_expression(l, errors)?.1 ^ eval_number_expression(r, errors)?.1,
-        )),
-        Expression::Power(loc, base, exp) => {
-            let b = eval_number_expression(base, errors)?.1;
-            let mut e = eval_number_expression(exp, errors)?.1;
-
-            if e.sign() == Sign::Minus {
-                errors.push(Output::error(
-                    expr.loc(),
-                    "power cannot take negative number as exponent".to_string(),
-                ));
-
-                Err(())
-            } else if e.sign() == Sign::NoSign {
-                Ok((*loc, BigInt::one()))
-            } else {
-                let mut res = b.clone();
-                e -= BigInt::one();
-                while e.sign() == Sign::Plus {
-                    res *= b.clone();
-                    e -= BigInt::one();
-                }
-                Ok((*loc, res))
-            }
-        }
-        Expression::ShiftLeft(loc, left, right) => {
-            let l = eval_number_expression(left, errors)?.1;
-            let r = eval_number_expression(right, errors)?.1;
-            let r = match r.to_usize() {
-                Some(r) => r,
-                None => {
-                    errors.push(Output::error(
-                        expr.loc(),
-                        format!("cannot left shift by {}", r),
-                    ));
-
-                    return Err(());
-                }
-            };
-            Ok((*loc, l << r))
-        }
-        Expression::ShiftRight(loc, left, right, _) => {
-            let l = eval_number_expression(left, errors)?.1;
-            let r = eval_number_expression(right, errors)?.1;
-            let r = match r.to_usize() {
-                Some(r) => r,
-                None => {
-                    errors.push(Output::error(
-                        expr.loc(),
-                        format!("cannot right shift by {}", r),
-                    ));
-
-                    return Err(());
-                }
-            };
-            Ok((*loc, l >> r))
-        }
-        Expression::NumberLiteral(loc, _, n) => Ok((*loc, n.clone())),
-        Expression::ZeroExt(loc, _, n) => Ok((*loc, eval_number_expression(n, errors)?.1)),
-        Expression::Not(loc, n) => Ok((*loc, !eval_number_expression(n, errors)?.1)),
-        Expression::Complement(loc, n) => Ok((*loc, !eval_number_expression(n, errors)?.1)),
-        Expression::UnaryMinus(loc, n) => Ok((*loc, -eval_number_expression(n, errors)?.1)),
-        _ => {
-            errors.push(Output::error(
-                expr.loc(),
-                "expression not allowed in constant number expression".to_string(),
-            ));
-
-            Err(())
-        }
-    }
-}

+ 0 - 5604
src/resolver/expression.rs

@@ -1,5604 +0,0 @@
-use num_bigint::BigInt;
-use num_bigint::Sign;
-use num_traits::FromPrimitive;
-use num_traits::Num;
-use num_traits::One;
-use num_traits::Pow;
-use num_traits::ToPrimitive;
-use num_traits::Zero;
-use std::cmp;
-use std::cmp::Ordering;
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::ops::Mul;
-use std::ops::Shl;
-use std::ops::Sub;
-
-use hex;
-use output;
-use output::Output;
-use parser::pt;
-use parser::pt::Loc;
-use resolver;
-use resolver::address::to_hexstr_eip55;
-use resolver::cfg::{resolve_var_decl_ty, ControlFlowGraph, Instr, Storage, Vartable};
-use resolver::eval::eval_number_expression;
-use resolver::storage::{
-    array_offset, array_pop, array_push, bytes_pop, bytes_push, delete, mapping_subscript,
-};
-
-#[derive(PartialEq, Clone, Debug)]
-pub enum Expression {
-    FunctionArg(Loc, usize),
-    BoolLiteral(Loc, bool),
-    BytesLiteral(Loc, Vec<u8>),
-    CodeLiteral(Loc, usize, bool),
-    NumberLiteral(Loc, u16, BigInt),
-    StructLiteral(Loc, resolver::Type, Vec<Expression>),
-    ArrayLiteral(Loc, resolver::Type, Vec<u32>, Vec<Expression>),
-    ConstArrayLiteral(Loc, Vec<u32>, Vec<Expression>),
-    Add(Loc, Box<Expression>, Box<Expression>),
-    Subtract(Loc, Box<Expression>, Box<Expression>),
-    Multiply(Loc, Box<Expression>, Box<Expression>),
-    UDivide(Loc, Box<Expression>, Box<Expression>),
-    SDivide(Loc, Box<Expression>, Box<Expression>),
-    UModulo(Loc, Box<Expression>, Box<Expression>),
-    SModulo(Loc, Box<Expression>, Box<Expression>),
-    Power(Loc, Box<Expression>, Box<Expression>),
-    BitwiseOr(Loc, Box<Expression>, Box<Expression>),
-    BitwiseAnd(Loc, Box<Expression>, Box<Expression>),
-    BitwiseXor(Loc, Box<Expression>, Box<Expression>),
-    ShiftLeft(Loc, Box<Expression>, Box<Expression>),
-    ShiftRight(Loc, Box<Expression>, Box<Expression>, bool),
-    Variable(Loc, usize),
-    Load(Loc, Box<Expression>),
-    StorageLoad(Loc, resolver::Type, Box<Expression>),
-    ZeroExt(Loc, resolver::Type, Box<Expression>),
-    SignExt(Loc, resolver::Type, Box<Expression>),
-    Trunc(Loc, resolver::Type, Box<Expression>),
-
-    UMore(Loc, Box<Expression>, Box<Expression>),
-    ULess(Loc, Box<Expression>, Box<Expression>),
-    UMoreEqual(Loc, Box<Expression>, Box<Expression>),
-    ULessEqual(Loc, Box<Expression>, Box<Expression>),
-    SMore(Loc, Box<Expression>, Box<Expression>),
-    SLess(Loc, Box<Expression>, Box<Expression>),
-    SMoreEqual(Loc, Box<Expression>, Box<Expression>),
-    SLessEqual(Loc, Box<Expression>, Box<Expression>),
-    Equal(Loc, Box<Expression>, Box<Expression>),
-    NotEqual(Loc, Box<Expression>, Box<Expression>),
-
-    Not(Loc, Box<Expression>),
-    Complement(Loc, Box<Expression>),
-    UnaryMinus(Loc, Box<Expression>),
-
-    Ternary(Loc, Box<Expression>, Box<Expression>, Box<Expression>),
-    ArraySubscript(Loc, Box<Expression>, Box<Expression>),
-    StructMember(Loc, Box<Expression>, usize),
-
-    AllocDynamicArray(Loc, resolver::Type, Box<Expression>, Option<Vec<u8>>),
-    DynamicArrayLength(Loc, Box<Expression>),
-    DynamicArraySubscript(Loc, Box<Expression>, resolver::Type, Box<Expression>),
-    StorageBytesSubscript(Loc, Box<Expression>, Box<Expression>),
-    StorageBytesPush(Loc, Box<Expression>, Box<Expression>),
-    StorageBytesPop(Loc, Box<Expression>),
-    StorageBytesLength(Loc, Box<Expression>),
-    StringCompare(Loc, StringLocation, StringLocation),
-    StringConcat(Loc, StringLocation, StringLocation),
-
-    Or(Loc, Box<Expression>, Box<Expression>),
-    And(Loc, Box<Expression>, Box<Expression>),
-    LocalFunctionCall(Loc, usize, Vec<Expression>),
-    ExternalFunctionCall {
-        loc: pt::Loc,
-        contract_no: usize,
-        function_no: usize,
-        address: Box<Expression>,
-        args: Vec<Expression>,
-        value: Box<Expression>,
-        gas: Box<Expression>,
-    },
-    Constructor {
-        loc: Loc,
-        contract_no: usize,
-        constructor_no: usize,
-        args: Vec<Expression>,
-        gas: Box<Expression>,
-        value: Option<Box<Expression>>,
-        salt: Option<Box<Expression>>,
-    },
-    Keccak256(Loc, Vec<(Expression, resolver::Type)>),
-
-    ReturnData(Loc),
-    GetAddress(Loc),
-    Balance(Loc, Box<Expression>),
-    Poison,
-    Unreachable,
-}
-
-#[derive(PartialEq, Clone, Debug)]
-pub enum StringLocation {
-    CompileTime(Vec<u8>),
-    RunTime(Box<Expression>),
-}
-
-impl Expression {
-    /// Return the location for this expression
-    pub fn loc(&self) -> Loc {
-        match self {
-            Expression::FunctionArg(loc, _)
-            | Expression::BoolLiteral(loc, _)
-            | Expression::BytesLiteral(loc, _)
-            | Expression::CodeLiteral(loc, _, _)
-            | Expression::NumberLiteral(loc, _, _)
-            | Expression::StructLiteral(loc, _, _)
-            | Expression::ArrayLiteral(loc, _, _, _)
-            | Expression::ConstArrayLiteral(loc, _, _)
-            | Expression::Add(loc, _, _)
-            | Expression::Subtract(loc, _, _)
-            | Expression::Multiply(loc, _, _)
-            | Expression::UDivide(loc, _, _)
-            | Expression::SDivide(loc, _, _)
-            | Expression::UModulo(loc, _, _)
-            | Expression::SModulo(loc, _, _)
-            | Expression::Power(loc, _, _)
-            | Expression::BitwiseOr(loc, _, _)
-            | Expression::BitwiseAnd(loc, _, _)
-            | Expression::BitwiseXor(loc, _, _)
-            | Expression::ShiftLeft(loc, _, _)
-            | Expression::ShiftRight(loc, _, _, _)
-            | Expression::Variable(loc, _)
-            | Expression::Load(loc, _)
-            | Expression::StorageLoad(loc, _, _)
-            | Expression::ZeroExt(loc, _, _)
-            | Expression::SignExt(loc, _, _)
-            | Expression::Trunc(loc, _, _)
-            | Expression::UMore(loc, _, _)
-            | Expression::ULess(loc, _, _)
-            | Expression::UMoreEqual(loc, _, _)
-            | Expression::ULessEqual(loc, _, _)
-            | Expression::SMore(loc, _, _)
-            | Expression::SLess(loc, _, _)
-            | Expression::SMoreEqual(loc, _, _)
-            | Expression::SLessEqual(loc, _, _)
-            | Expression::Equal(loc, _, _)
-            | Expression::NotEqual(loc, _, _)
-            | Expression::Not(loc, _)
-            | Expression::Complement(loc, _)
-            | Expression::UnaryMinus(loc, _)
-            | Expression::Ternary(loc, _, _, _)
-            | Expression::ArraySubscript(loc, _, _)
-            | Expression::StructMember(loc, _, _)
-            | Expression::Or(loc, _, _)
-            | Expression::AllocDynamicArray(loc, _, _, _)
-            | Expression::DynamicArrayLength(loc, _)
-            | Expression::DynamicArraySubscript(loc, _, _, _)
-            | Expression::StorageBytesSubscript(loc, _, _)
-            | Expression::StorageBytesPush(loc, _, _)
-            | Expression::StorageBytesPop(loc, _)
-            | Expression::StorageBytesLength(loc, _)
-            | Expression::StringCompare(loc, _, _)
-            | Expression::StringConcat(loc, _, _)
-            | Expression::Keccak256(loc, _)
-            | Expression::ReturnData(loc)
-            | Expression::LocalFunctionCall(loc, _, _)
-            | Expression::ExternalFunctionCall { loc, .. }
-            | Expression::Constructor { loc, .. }
-            | Expression::GetAddress(loc)
-            | Expression::Balance(loc, _)
-            | Expression::And(loc, _, _) => *loc,
-            Expression::Poison | Expression::Unreachable => unreachable!(),
-        }
-    }
-    /// Returns true if the Expression may load from contract storage using StorageLoad
-    pub fn reads_contract_storage(&self) -> bool {
-        match self {
-            Expression::StorageLoad(_, _, _) => true,
-            Expression::FunctionArg(_, _)
-            | Expression::BoolLiteral(_, _)
-            | Expression::BytesLiteral(_, _)
-            | Expression::CodeLiteral(_, _, _)
-            | Expression::NumberLiteral(_, _, _) => false,
-            Expression::StructLiteral(_, _, exprs) => {
-                exprs.iter().any(|e| e.reads_contract_storage())
-            }
-            Expression::ArrayLiteral(_, _, _, exprs) => {
-                exprs.iter().any(|e| e.reads_contract_storage())
-            }
-            Expression::ConstArrayLiteral(_, _, _) => false,
-            Expression::Add(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::Subtract(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::Multiply(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::UDivide(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::SDivide(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::UModulo(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::SModulo(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-
-            Expression::Power(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::BitwiseOr(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::BitwiseAnd(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::BitwiseXor(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::ShiftLeft(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::ShiftRight(_, l, r, _) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-
-            Expression::Variable(_, _) | Expression::Load(_, _) => false,
-            Expression::ZeroExt(_, _, e) => e.reads_contract_storage(),
-            Expression::SignExt(_, _, e) => e.reads_contract_storage(),
-            Expression::Trunc(_, _, e) => e.reads_contract_storage(),
-
-            Expression::UMore(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::ULess(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::UMoreEqual(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::ULessEqual(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::SMore(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::SLess(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::SLessEqual(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::SMoreEqual(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::Equal(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::NotEqual(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-
-            Expression::Not(_, e) => e.reads_contract_storage(),
-            Expression::Complement(_, e) => e.reads_contract_storage(),
-            Expression::UnaryMinus(_, e) => e.reads_contract_storage(),
-
-            Expression::Ternary(_, c, l, r) => {
-                c.reads_contract_storage()
-                    || l.reads_contract_storage()
-                    || r.reads_contract_storage()
-            }
-            Expression::DynamicArraySubscript(_, l, _, r) | Expression::ArraySubscript(_, l, r) => {
-                l.reads_contract_storage() || r.reads_contract_storage()
-            }
-            Expression::DynamicArrayLength(_, e) | Expression::AllocDynamicArray(_, _, e, _) => {
-                e.reads_contract_storage()
-            }
-            Expression::StorageBytesSubscript(_, _, _)
-            | Expression::StorageBytesPush(_, _, _)
-            | Expression::StorageBytesPop(_, _)
-            | Expression::StorageBytesLength(_, _) => true,
-            Expression::StructMember(_, s, _) => s.reads_contract_storage(),
-            Expression::LocalFunctionCall(_, _, args)
-            | Expression::Constructor { args, .. }
-            | Expression::ExternalFunctionCall { args, .. } => {
-                args.iter().any(|a| a.reads_contract_storage())
-            }
-            Expression::Keccak256(_, e) => e.iter().any(|e| e.0.reads_contract_storage()),
-            Expression::And(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::Or(_, l, r) => l.reads_contract_storage() || r.reads_contract_storage(),
-            Expression::StringConcat(_, l, r) | Expression::StringCompare(_, l, r) => {
-                if let StringLocation::RunTime(e) = l {
-                    if !e.reads_contract_storage() {
-                        return false;
-                    }
-                }
-                if let StringLocation::RunTime(e) = r {
-                    return e.reads_contract_storage();
-                }
-                false
-            }
-            Expression::ReturnData(_) => false,
-            Expression::GetAddress(_) => false,
-            Expression::Balance(_, s) => s.reads_contract_storage(),
-            Expression::Poison => false,
-            Expression::Unreachable => false,
-        }
-    }
-
-    /// Is this expression 0
-    fn const_zero(&self) -> bool {
-        let mut nullsink = Vec::new();
-
-        if let Ok((_, value)) = eval_number_expression(&self, &mut nullsink) {
-            value == BigInt::zero()
-        } else {
-            false
-        }
-    }
-}
-
-/// Unescape a string literal
-fn unescape(literal: &str, start: usize, errors: &mut Vec<output::Output>) -> String {
-    let mut s = String::new();
-    let mut indeces = literal.char_indices();
-
-    while let Some((_, ch)) = indeces.next() {
-        if ch != '\\' {
-            s.push(ch);
-            continue;
-        }
-
-        match indeces.next() {
-            Some((_, '\n')) => (),
-            Some((_, '\\')) => s.push('\\'),
-            Some((_, '\'')) => s.push('\''),
-            Some((_, '"')) => s.push('"'),
-            Some((_, 'b')) => s.push('\u{0008}'),
-            Some((_, 'f')) => s.push('\u{000c}'),
-            Some((_, 'n')) => s.push('\n'),
-            Some((_, 'r')) => s.push('\r'),
-            Some((_, 't')) => s.push('\t'),
-            Some((_, 'v')) => s.push('\u{000b}'),
-            Some((i, 'x')) => match get_digits(&mut indeces, 2) {
-                Ok(ch) => match std::char::from_u32(ch) {
-                    Some(ch) => s.push(ch),
-                    None => {
-                        errors.push(Output::error(
-                            pt::Loc(start + i, start + i + 4),
-                            format!("\\x{:02x} is not a valid unicode character", ch),
-                        ));
-                    }
-                },
-                Err(offset) => {
-                    errors.push(Output::error(
-                        pt::Loc(start + i, start + std::cmp::min(literal.len(), offset)),
-                        "\\x escape should be followed by two hex digits".to_string(),
-                    ));
-                }
-            },
-            Some((i, 'u')) => match get_digits(&mut indeces, 4) {
-                Ok(ch) => match std::char::from_u32(ch) {
-                    Some(ch) => s.push(ch),
-                    None => {
-                        errors.push(Output::error(
-                            pt::Loc(start + i, start + i + 6),
-                            format!("\\u{:04x} is not a valid unicode character", ch),
-                        ));
-                    }
-                },
-                Err(offset) => {
-                    errors.push(Output::error(
-                        pt::Loc(start + i, start + std::cmp::min(literal.len(), offset)),
-                        "\\u escape should be followed by four hex digits".to_string(),
-                    ));
-                }
-            },
-            Some((i, ch)) => {
-                errors.push(Output::error(
-                    pt::Loc(start + i, start + i + ch.len_utf8()),
-                    format!("unknown escape character '{}'", ch),
-                ));
-            }
-            None => unreachable!(),
-        }
-    }
-
-    s
-}
-
-/// Get the hex digits for an escaped \x or \u. Returns either the value or
-/// or the offset of the last character
-fn get_digits(input: &mut std::str::CharIndices, len: usize) -> Result<u32, usize> {
-    let mut n = 0;
-    let offset;
-
-    for _ in 0..len {
-        if let Some((_, ch)) = input.next() {
-            if let Some(v) = ch.to_digit(16) {
-                n = (n << 4) + v;
-                continue;
-            }
-            offset = match input.next() {
-                Some((i, _)) => i,
-                None => std::usize::MAX,
-            };
-        } else {
-            offset = std::usize::MAX;
-        }
-
-        return Err(offset);
-    }
-
-    Ok(n)
-}
-
-fn coerce(
-    l: &resolver::Type,
-    l_loc: &pt::Loc,
-    r: &resolver::Type,
-    r_loc: &pt::Loc,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<resolver::Type, ()> {
-    let l = match l {
-        resolver::Type::Ref(ty) => ty,
-        resolver::Type::StorageRef(ty) => ty,
-        _ => l,
-    };
-    let r = match r {
-        resolver::Type::Ref(ty) => ty,
-        resolver::Type::StorageRef(ty) => ty,
-        _ => r,
-    };
-
-    if *l == *r {
-        return Ok(l.clone());
-    }
-
-    coerce_int(l, l_loc, r, r_loc, true, ns, errors)
-}
-
-fn get_int_length(
-    l: &resolver::Type,
-    l_loc: &pt::Loc,
-    allow_bytes: bool,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<(u16, bool), ()> {
-    match l {
-        resolver::Type::Uint(n) => Ok((*n, false)),
-        resolver::Type::Int(n) => Ok((*n, true)),
-        resolver::Type::Bytes(n) if allow_bytes => Ok((*n as u16 * 8, false)),
-        resolver::Type::Enum(n) => {
-            errors.push(Output::error(
-                *l_loc,
-                format!("type enum {} not allowed", ns.enums[*n].print_to_string(),),
-            ));
-            Err(())
-        }
-        resolver::Type::Struct(n) => {
-            errors.push(Output::error(
-                *l_loc,
-                format!(
-                    "type struct {} not allowed",
-                    ns.structs[*n].print_to_string()
-                ),
-            ));
-            Err(())
-        }
-        resolver::Type::Array(_, _) => {
-            errors.push(Output::error(
-                *l_loc,
-                format!("type array {} not allowed", l.to_string(ns)),
-            ));
-            Err(())
-        }
-        resolver::Type::Ref(n) => get_int_length(n, l_loc, allow_bytes, ns, errors),
-        resolver::Type::StorageRef(n) => get_int_length(n, l_loc, allow_bytes, ns, errors),
-        resolver::Type::Undef => {
-            unreachable!();
-        }
-        _ => {
-            errors.push(Output::error(
-                *l_loc,
-                format!("expression of type {} not allowed", l.to_string(ns)),
-            ));
-            Err(())
-        }
-    }
-}
-
-fn coerce_int(
-    l: &resolver::Type,
-    l_loc: &pt::Loc,
-    r: &resolver::Type,
-    r_loc: &pt::Loc,
-    allow_bytes: bool,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<resolver::Type, ()> {
-    let l = match l {
-        resolver::Type::Ref(ty) => ty,
-        resolver::Type::StorageRef(ty) => ty,
-        _ => l,
-    };
-    let r = match r {
-        resolver::Type::Ref(ty) => ty,
-        resolver::Type::StorageRef(ty) => ty,
-        _ => r,
-    };
-
-    match (l, r) {
-        (resolver::Type::Bytes(left_length), resolver::Type::Bytes(right_length))
-            if allow_bytes =>
-        {
-            return Ok(resolver::Type::Bytes(std::cmp::max(
-                *left_length,
-                *right_length,
-            )));
-        }
-        _ => (),
-    }
-
-    let (left_len, left_signed) = get_int_length(l, l_loc, false, ns, errors)?;
-
-    let (right_len, right_signed) = get_int_length(r, r_loc, false, ns, errors)?;
-
-    Ok(match (left_signed, right_signed) {
-        (true, true) => resolver::Type::Int(cmp::max(left_len, right_len)),
-        (false, false) => resolver::Type::Uint(cmp::max(left_len, right_len)),
-        (true, false) => resolver::Type::Int(cmp::max(left_len, cmp::min(right_len + 8, 256))),
-        (false, true) => resolver::Type::Int(cmp::max(cmp::min(left_len + 8, 256), right_len)),
-    })
-}
-
-/// Try to convert a BigInt into a Expression::NumberLiteral. This checks for sign,
-/// width and creates to correct Type.
-fn bigint_to_expression(
-    loc: &pt::Loc,
-    n: &BigInt,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    // Return smallest type
-    let bits = n.bits();
-
-    let int_size = if bits < 7 { 8 } else { (bits + 7) & !7 } as u16;
-
-    if n.sign() == Sign::Minus {
-        if bits > 255 {
-            errors.push(Output::error(*loc, format!("{} is too large", n)));
-            Err(())
-        } else {
-            Ok((
-                Expression::NumberLiteral(*loc, int_size, n.clone()),
-                resolver::Type::Int(int_size),
-            ))
-        }
-    } else if bits > 256 {
-        errors.push(Output::error(*loc, format!("{} is too large", n)));
-        Err(())
-    } else {
-        Ok((
-            Expression::NumberLiteral(*loc, int_size, n.clone()),
-            resolver::Type::Uint(int_size),
-        ))
-    }
-}
-
-/// Cast from one type to another, which also automatically derefs any Type::Ref() type.
-/// if the cast is explicit (e.g. bytes32(bar) then implicit should be set to false.
-pub fn cast(
-    loc: &pt::Loc,
-    expr: Expression,
-    from: &resolver::Type,
-    to: &resolver::Type,
-    implicit: bool,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<Expression, ()> {
-    if from == to {
-        return Ok(expr);
-    }
-
-    // First of all, if we have a ref then derefence it
-    if let resolver::Type::Ref(r) = from {
-        return cast(
-            loc,
-            Expression::Load(*loc, Box::new(expr)),
-            r,
-            to,
-            implicit,
-            ns,
-            errors,
-        );
-    }
-
-    // If it's a storage reference then load the value. The expr is the storage slot
-    if let resolver::Type::StorageRef(r) = from {
-        if let Expression::StorageBytesSubscript(_, _, _) = expr {
-            return cast(loc, expr, r, to, implicit, ns, errors);
-        } else {
-            return cast(
-                loc,
-                Expression::StorageLoad(*loc, *r.clone(), Box::new(expr)),
-                r,
-                to,
-                implicit,
-                ns,
-                errors,
-            );
-        }
-    }
-
-    if from == to {
-        return Ok(expr);
-    }
-
-    let (from_conv, to_conv) = {
-        if implicit {
-            (from.clone(), to.clone())
-        } else {
-            let from_conv = if let resolver::Type::Enum(n) = from {
-                ns.enums[*n].ty.clone()
-            } else {
-                from.clone()
-            };
-
-            let to_conv = if let resolver::Type::Enum(n) = to {
-                ns.enums[*n].ty.clone()
-            } else {
-                to.clone()
-            };
-
-            (from_conv, to_conv)
-        }
-    };
-
-    // Special case: when converting literal sign can change if it fits
-    match (&expr, &from_conv, &to_conv) {
-        (&Expression::NumberLiteral(_, _, ref n), p, &resolver::Type::Uint(to_len))
-            if p.is_primitive() =>
-        {
-            return if n.sign() == Sign::Minus {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion cannot change negative number to {}",
-                        to.to_string(ns)
-                    ),
-                ));
-
-                Err(())
-            } else if n.bits() >= to_len as usize {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion would truncate from {} to {}",
-                        from.to_string(ns),
-                        to.to_string(ns)
-                    ),
-                ));
-
-                Err(())
-            } else {
-                Ok(Expression::NumberLiteral(*loc, to_len, n.clone()))
-            }
-        }
-        (&Expression::NumberLiteral(_, _, ref n), p, &resolver::Type::Int(to_len))
-            if p.is_primitive() =>
-        {
-            return if n.bits() >= to_len as usize {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion would truncate from {} to {}",
-                        from.to_string(ns),
-                        to.to_string(ns)
-                    ),
-                ));
-
-                Err(())
-            } else {
-                Ok(Expression::NumberLiteral(*loc, to_len, n.clone()))
-            }
-        }
-        // Literal strings can be implicitly lengthened
-        (&Expression::BytesLiteral(_, ref bs), p, &resolver::Type::Bytes(to_len))
-            if p.is_primitive() =>
-        {
-            return if bs.len() > to_len as usize {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion would truncate from {} to {}",
-                        from.to_string(ns),
-                        to.to_string(ns)
-                    ),
-                ));
-
-                Err(())
-            } else {
-                let mut bs = bs.to_owned();
-
-                // Add zero's at the end as needed
-                bs.resize(to_len as usize, 0);
-
-                Ok(Expression::BytesLiteral(*loc, bs))
-            };
-        }
-        (&Expression::BytesLiteral(loc, ref init), _, &resolver::Type::DynamicBytes)
-        | (&Expression::BytesLiteral(loc, ref init), _, &resolver::Type::String) => {
-            return Ok(Expression::AllocDynamicArray(
-                loc,
-                to_conv,
-                Box::new(Expression::NumberLiteral(loc, 32, BigInt::from(init.len()))),
-                Some(init.clone()),
-            ));
-        }
-        _ => (),
-    };
-
-    cast_types(
-        loc, expr, from_conv, to_conv, from, to, implicit, ns, errors,
-    )
-}
-
-/// Do casting between types (no literals)
-fn cast_types(
-    loc: &pt::Loc,
-    expr: Expression,
-    from_conv: resolver::Type,
-    to_conv: resolver::Type,
-    from: &resolver::Type,
-    to: &resolver::Type,
-    implicit: bool,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<Expression, ()> {
-    let address_bits = ns.address_length as u16 * 8;
-
-    #[allow(clippy::comparison_chain)]
-    match (from_conv, to_conv) {
-        (resolver::Type::Bytes(1), resolver::Type::Uint(8)) => Ok(expr),
-        (resolver::Type::Uint(8), resolver::Type::Bytes(1)) => Ok(expr),
-        (resolver::Type::Uint(from_len), resolver::Type::Uint(to_len)) => {
-            match from_len.cmp(&to_len) {
-                Ordering::Greater => {
-                    if implicit {
-                        errors.push(Output::type_error(
-                            *loc,
-                            format!(
-                                "implicit conversion would truncate from {} to {}",
-                                from.to_string(ns),
-                                to.to_string(ns)
-                            ),
-                        ));
-                        Err(())
-                    } else {
-                        Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
-                    }
-                }
-                Ordering::Less => Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr))),
-                Ordering::Equal => Ok(expr),
-            }
-        }
-        (resolver::Type::Int(from_len), resolver::Type::Int(to_len)) => match from_len.cmp(&to_len)
-        {
-            Ordering::Greater => {
-                if implicit {
-                    errors.push(Output::type_error(
-                        *loc,
-                        format!(
-                            "implicit conversion would truncate from {} to {}",
-                            from.to_string(ns),
-                            to.to_string(ns)
-                        ),
-                    ));
-                    Err(())
-                } else {
-                    Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
-                }
-            }
-            Ordering::Less => Ok(Expression::SignExt(*loc, to.clone(), Box::new(expr))),
-            Ordering::Equal => Ok(expr),
-        },
-        (resolver::Type::Uint(from_len), resolver::Type::Int(to_len)) if to_len > from_len => {
-            Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
-        }
-        (resolver::Type::Int(from_len), resolver::Type::Uint(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion would change sign from {} to {}",
-                        from.to_string(ns),
-                        to.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if from_len > to_len {
-                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
-            } else if from_len < to_len {
-                Ok(Expression::SignExt(*loc, to.clone(), Box::new(expr)))
-            } else {
-                Ok(expr)
-            }
-        }
-        (resolver::Type::Uint(from_len), resolver::Type::Int(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion would change sign from {} to {}",
-                        from.to_string(ns),
-                        to.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if from_len > to_len {
-                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
-            } else if from_len < to_len {
-                Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
-            } else {
-                Ok(expr)
-            }
-        }
-        // Casting int to address
-        (resolver::Type::Uint(from_len), resolver::Type::Address(_))
-        | (resolver::Type::Int(from_len), resolver::Type::Address(_)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion from {} to address not allowed",
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if from_len > address_bits {
-                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
-            } else if from_len < address_bits {
-                Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
-            } else {
-                Ok(expr)
-            }
-        }
-        // Casting int address to int
-        (resolver::Type::Address(_), resolver::Type::Uint(to_len))
-        | (resolver::Type::Address(_), resolver::Type::Int(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion to {} from address not allowed",
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if to_len < address_bits {
-                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
-            } else if to_len > address_bits {
-                Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
-            } else {
-                Ok(expr)
-            }
-        }
-        // Lengthing or shorting a fixed bytes array
-        (resolver::Type::Bytes(from_len), resolver::Type::Bytes(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion would truncate from {} to {}",
-                        from.to_string(ns),
-                        to.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if to_len > from_len {
-                let shift = (to_len - from_len) * 8;
-
-                Ok(Expression::ShiftLeft(
-                    *loc,
-                    Box::new(Expression::ZeroExt(*loc, to.clone(), Box::new(expr))),
-                    Box::new(Expression::NumberLiteral(
-                        *loc,
-                        to_len as u16 * 8,
-                        BigInt::from_u8(shift).unwrap(),
-                    )),
-                ))
-            } else {
-                let shift = (from_len - to_len) * 8;
-
-                Ok(Expression::Trunc(
-                    *loc,
-                    to.clone(),
-                    Box::new(Expression::ShiftRight(
-                        *loc,
-                        Box::new(expr),
-                        Box::new(Expression::NumberLiteral(
-                            *loc,
-                            from_len as u16 * 8,
-                            BigInt::from_u8(shift).unwrap(),
-                        )),
-                        false,
-                    )),
-                ))
-            }
-        }
-        // Explicit conversion from bytesN to int/uint only allowed with expliciy
-        // cast and if it is the same size (i.e. no conversion required)
-        (resolver::Type::Bytes(from_len), resolver::Type::Uint(to_len))
-        | (resolver::Type::Bytes(from_len), resolver::Type::Int(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if from_len as u16 * 8 != to_len {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else {
-                Ok(expr)
-            }
-        }
-        // Explicit conversion to bytesN from int/uint only allowed with expliciy
-        // cast and if it is the same size (i.e. no conversion required)
-        (resolver::Type::Uint(from_len), resolver::Type::Bytes(to_len))
-        | (resolver::Type::Int(from_len), resolver::Type::Bytes(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if to_len as u16 * 8 != from_len {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else {
-                Ok(expr)
-            }
-        }
-        // Explicit conversion from bytesN to address only allowed with expliciy
-        // cast and if it is the same size (i.e. no conversion required)
-        (resolver::Type::Bytes(from_len), resolver::Type::Address(_)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if from_len as usize != ns.address_length {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else {
-                Ok(expr)
-            }
-        }
-        // Explicit conversion between contract and address is allowed
-        (resolver::Type::Address(false), resolver::Type::Address(true))
-        | (resolver::Type::Address(_), resolver::Type::Contract(_))
-        | (resolver::Type::Contract(_), resolver::Type::Address(_)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else {
-                Ok(expr)
-            }
-        }
-        // conversion from address payable to address is implicitly allowed (not vice versa)
-        (resolver::Type::Address(true), resolver::Type::Address(false)) => Ok(expr),
-        // Explicit conversion to bytesN from int/uint only allowed with expliciy
-        // cast and if it is the same size (i.e. no conversion required)
-        (resolver::Type::Address(_), resolver::Type::Bytes(to_len)) => {
-            if implicit {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "implicit conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else if to_len as usize != ns.address_length {
-                errors.push(Output::type_error(
-                    *loc,
-                    format!(
-                        "conversion to {} from {} not allowed",
-                        to.to_string(ns),
-                        from.to_string(ns)
-                    ),
-                ));
-                Err(())
-            } else {
-                Ok(expr)
-            }
-        }
-        (resolver::Type::String, resolver::Type::DynamicBytes)
-        | (resolver::Type::DynamicBytes, resolver::Type::String)
-            if !implicit =>
-        {
-            Ok(expr)
-        }
-        // string conversions
-        /*
-        (resolver::Type::Bytes(_), resolver::Type::String) => Ok(expr),
-        (resolver::Type::String, resolver::Type::Bytes(to_len)) => {
-            if let Expression::BytesLiteral(_, from_str) = &expr {
-                if from_str.len() > to_len as usize {
-                    errors.push(Output::type_error(
-                        *loc,
-                        format!(
-                            "string of {} bytes is too long to fit into {}",
-                            from_str.len(),
-                            to.to_string(ns)
-                        ),
-                    ));
-                    return Err(());
-                }
-            }
-            Ok(expr)
-        }
-        */
-        (resolver::Type::Undef, _) => {
-            errors.push(Output::type_error(
-                *loc,
-                "function or method does not return a value".to_string(),
-            ));
-            Err(())
-        }
-        _ => {
-            errors.push(Output::type_error(
-                *loc,
-                format!(
-                    "conversion from {} to {} not possible",
-                    from.to_string(ns),
-                    to.to_string(ns)
-                ),
-            ));
-            Err(())
-        }
-    }
-}
-
-pub fn expression(
-    expr: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    match expr {
-        pt::Expression::ArrayLiteral(loc, exprs) => {
-            resolve_array_literal(loc, exprs, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Expression::BoolLiteral(loc, v) => {
-            Ok((Expression::BoolLiteral(*loc, *v), resolver::Type::Bool))
-        }
-        pt::Expression::StringLiteral(v) => {
-            // Concatenate the strings
-            let mut result = Vec::new();
-            let mut loc = pt::Loc(v[0].loc.0, 0);
-
-            for s in v {
-                result.extend_from_slice(unescape(&s.string, s.loc.0, errors).as_bytes());
-                loc.1 = s.loc.1;
-            }
-
-            let length = result.len();
-
-            Ok((
-                Expression::BytesLiteral(loc, result),
-                resolver::Type::Bytes(length as u8),
-            ))
-        }
-        pt::Expression::HexLiteral(v) => {
-            let mut result = Vec::new();
-            let mut loc = pt::Loc(0, 0);
-
-            for s in v {
-                if (s.hex.len() % 2) != 0 {
-                    errors.push(Output::error(
-                        s.loc,
-                        format!("hex string \"{}\" has odd number of characters", s.hex),
-                    ));
-                    return Err(());
-                } else {
-                    result.extend_from_slice(&hex::decode(&s.hex).unwrap());
-                    if loc.0 == 0 {
-                        loc.0 = s.loc.0;
-                    }
-                    loc.1 = s.loc.1;
-                }
-            }
-
-            let length = result.len();
-
-            Ok((
-                Expression::BytesLiteral(loc, result),
-                resolver::Type::Bytes(length as u8),
-            ))
-        }
-        pt::Expression::NumberLiteral(loc, b) => bigint_to_expression(loc, b, errors),
-        pt::Expression::HexNumberLiteral(loc, n) => {
-            // ns.address_length is in bytes; double for hex and two for the leading 0x
-            let looks_like_address = n.len() == ns.address_length * 2 + 2
-                && n.starts_with("0x")
-                && !n.chars().any(|c| c == '_');
-
-            if looks_like_address {
-                let address = to_hexstr_eip55(n);
-
-                if address == *n {
-                    let s: String = address.chars().skip(2).collect();
-
-                    Ok((
-                        Expression::NumberLiteral(
-                            *loc,
-                            ns.address_length as u16 * 8,
-                            BigInt::from_str_radix(&s, 16).unwrap(),
-                        ),
-                        resolver::Type::Address(false),
-                    ))
-                } else {
-                    errors.push(Output::error(
-                        *loc,
-                        format!(
-                            "address literal has incorrect checksum, expected ‘{}’",
-                            address
-                        ),
-                    ));
-                    Err(())
-                }
-            } else {
-                // from_str_radix does not like the 0x prefix
-                let s: String = n.chars().filter(|v| *v != 'x' && *v != '_').collect();
-
-                bigint_to_expression(loc, &BigInt::from_str_radix(&s, 16).unwrap(), errors)
-            }
-        }
-        pt::Expression::Variable(id) => {
-            if let Some(ref mut tab) = *vartab {
-                let v = tab.find(id, contract_no.unwrap(), ns, errors)?;
-                match &v.storage {
-                    Storage::Contract(n) => Ok((
-                        Expression::NumberLiteral(id.loc, 256, n.clone()),
-                        resolver::Type::StorageRef(Box::new(v.ty)),
-                    )),
-                    Storage::Constant(n) => {
-                        cfg.add(
-                            tab,
-                            Instr::Constant {
-                                res: v.pos,
-                                constant: *n,
-                            },
-                        );
-                        Ok((Expression::Variable(id.loc, v.pos), v.ty))
-                    }
-                    Storage::Local => Ok((Expression::Variable(id.loc, v.pos), v.ty)),
-                }
-            } else {
-                errors.push(Output::error(
-                    id.loc,
-                    format!("cannot read variable ‘{}’ in constant expression", id.name),
-                ));
-                Err(())
-            }
-        }
-        pt::Expression::Add(loc, l, r) => addition(loc, l, r, cfg, contract_no, ns, vartab, errors),
-        pt::Expression::Subtract(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                false,
-                ns,
-                errors,
-            )?;
-
-            Ok((
-                Expression::Subtract(
-                    *loc,
-                    Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
-        pt::Expression::BitwiseOr(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            Ok((
-                Expression::BitwiseOr(
-                    *loc,
-                    Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
-        pt::Expression::BitwiseAnd(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            Ok((
-                Expression::BitwiseAnd(
-                    *loc,
-                    Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
-        pt::Expression::BitwiseXor(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            Ok((
-                Expression::BitwiseXor(
-                    *loc,
-                    Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
-        pt::Expression::ShiftLeft(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            // left hand side may be bytes/int/uint
-            // right hand size may be int/uint
-            let _ = get_int_length(&left_type, &l.loc(), true, ns, errors)?;
-            let (right_length, _) = get_int_length(&right_type, &r.loc(), false, ns, errors)?;
-
-            Ok((
-                Expression::ShiftLeft(
-                    *loc,
-                    Box::new(left),
-                    Box::new(cast_shift_arg(loc, right, right_length, &left_type, ns)),
-                ),
-                left_type,
-            ))
-        }
-        pt::Expression::ShiftRight(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            // left hand side may be bytes/int/uint
-            // right hand size may be int/uint
-            let _ = get_int_length(&left_type, &l.loc(), true, ns, errors)?;
-            let (right_length, _) = get_int_length(&right_type, &r.loc(), false, ns, errors)?;
-
-            Ok((
-                Expression::ShiftRight(
-                    *loc,
-                    Box::new(left),
-                    Box::new(cast_shift_arg(loc, right, right_length, &left_type, ns)),
-                    left_type.signed(),
-                ),
-                left_type,
-            ))
-        }
-        pt::Expression::Multiply(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                false,
-                ns,
-                errors,
-            )?;
-
-            Ok((
-                Expression::Multiply(
-                    *loc,
-                    Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
-        pt::Expression::Divide(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                false,
-                ns,
-                errors,
-            )?;
-
-            if ty.signed() {
-                Ok((
-                    Expression::SDivide(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    ty,
-                ))
-            } else {
-                Ok((
-                    Expression::UDivide(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    ty,
-                ))
-            }
-        }
-        pt::Expression::Modulo(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                false,
-                ns,
-                errors,
-            )?;
-
-            if ty.signed() {
-                Ok((
-                    Expression::SModulo(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    ty,
-                ))
-            } else {
-                Ok((
-                    Expression::UModulo(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    ty,
-                ))
-            }
-        }
-        pt::Expression::Power(loc, b, e) => {
-            let (base, base_type) = expression(b, cfg, contract_no, ns, vartab, errors)?;
-            let (exp, exp_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-            // solc-0.5.13 does not allow either base or exp to be signed
-            if base_type.signed() || exp_type.signed() {
-                errors.push(Output::error(
-                    *loc,
-                    "exponation (**) is not allowed with signed types".to_string(),
-                ));
-                return Err(());
-            }
-
-            let ty = coerce_int(&base_type, &b.loc(), &exp_type, &e.loc(), false, ns, errors)?;
-
-            Ok((
-                Expression::Power(
-                    *loc,
-                    Box::new(cast(&b.loc(), base, &base_type, &ty, true, ns, errors)?),
-                    Box::new(cast(&e.loc(), exp, &exp_type, &ty, true, ns, errors)?),
-                ),
-                ty,
-            ))
-        }
-
-        // compare
-        pt::Expression::More(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            if ty.signed() {
-                Ok((
-                    Expression::SMore(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            } else {
-                Ok((
-                    Expression::UMore(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            }
-        }
-        pt::Expression::Less(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            if ty.signed() {
-                Ok((
-                    Expression::SLess(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            } else {
-                Ok((
-                    Expression::ULess(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            }
-        }
-        pt::Expression::MoreEqual(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            if ty.signed() {
-                Ok((
-                    Expression::SMoreEqual(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            } else {
-                Ok((
-                    Expression::UMoreEqual(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            }
-        }
-        pt::Expression::LessEqual(loc, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-            let ty = coerce_int(
-                &left_type,
-                &l.loc(),
-                &right_type,
-                &r.loc(),
-                true,
-                ns,
-                errors,
-            )?;
-
-            if ty.signed() {
-                Ok((
-                    Expression::SLessEqual(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            } else {
-                Ok((
-                    Expression::ULessEqual(
-                        *loc,
-                        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-                        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-                    ),
-                    resolver::Type::Bool,
-                ))
-            }
-        }
-        pt::Expression::Equal(loc, l, r) => Ok((
-            equal(loc, l, r, cfg, contract_no, ns, vartab, errors)?,
-            resolver::Type::Bool,
-        )),
-        pt::Expression::NotEqual(loc, l, r) => Ok((
-            Expression::Not(
-                *loc,
-                Box::new(equal(loc, l, r, cfg, contract_no, ns, vartab, errors)?),
-            ),
-            resolver::Type::Bool,
-        )),
-        // unary expressions
-        pt::Expression::Not(loc, e) => {
-            let (expr, expr_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-            Ok((
-                Expression::Not(
-                    *loc,
-                    Box::new(cast(
-                        &loc,
-                        expr,
-                        &expr_type,
-                        &resolver::Type::Bool,
-                        true,
-                        ns,
-                        errors,
-                    )?),
-                ),
-                resolver::Type::Bool,
-            ))
-        }
-        pt::Expression::Complement(loc, e) => {
-            let (expr, expr_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-            get_int_length(&expr_type, loc, true, ns, errors)?;
-
-            Ok((Expression::Complement(*loc, Box::new(expr)), expr_type))
-        }
-        pt::Expression::UnaryMinus(loc, e) => {
-            let (expr, expr_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-            if let Expression::NumberLiteral(_, _, n) = expr {
-                bigint_to_expression(loc, &-n, errors)
-            } else {
-                get_int_length(&expr_type, loc, false, ns, errors)?;
-
-                Ok((Expression::UnaryMinus(*loc, Box::new(expr)), expr_type))
-            }
-        }
-        pt::Expression::UnaryPlus(loc, e) => {
-            let (expr, expr_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-            get_int_length(&expr_type, loc, false, ns, errors)?;
-
-            Ok((expr, expr_type))
-        }
-
-        pt::Expression::Ternary(loc, c, l, r) => {
-            let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-            let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-            let (cond, cond_type) = expression(c, cfg, contract_no, ns, vartab, errors)?;
-
-            let cond = cast(
-                &c.loc(),
-                cond,
-                &cond_type,
-                &resolver::Type::Bool,
-                true,
-                ns,
-                errors,
-            )?;
-
-            let ty = coerce(&left_type, &l.loc(), &right_type, &r.loc(), ns, errors)?;
-
-            Ok((
-                Expression::Ternary(*loc, Box::new(cond), Box::new(left), Box::new(right)),
-                ty,
-            ))
-        }
-
-        // pre/post decrement/increment
-        pt::Expression::PostIncrement(loc, var)
-        | pt::Expression::PreIncrement(loc, var)
-        | pt::Expression::PostDecrement(loc, var)
-        | pt::Expression::PreDecrement(loc, var) => {
-            let id = match var.as_ref() {
-                pt::Expression::Variable(id) => id,
-                _ => unreachable!(),
-            };
-
-            let vartab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("cannot access variable {} in constant expression", id.name),
-                    ));
-                    return Err(());
-                }
-            };
-
-            let v = vartab.find(id, contract_no.unwrap(), ns, errors)?;
-
-            match v.ty {
-                resolver::Type::Bytes(_) | resolver::Type::Int(_) | resolver::Type::Uint(_) => (),
-                _ => {
-                    errors.push(Output::error(
-                        var.loc(),
-                        format!(
-                            "variable ‘{}’ of incorrect type {}",
-                            id.name.to_string(),
-                            v.ty.to_string(ns)
-                        ),
-                    ));
-                    return Err(());
-                }
-            };
-
-            let lvalue = match &v.storage {
-                Storage::Contract(n) => Expression::StorageLoad(
-                    *loc,
-                    v.ty.clone(),
-                    Box::new(Expression::NumberLiteral(*loc, 256, n.clone())),
-                ),
-                Storage::Constant(_) => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("cannot assign to constant ‘{}’", id.name),
-                    ));
-                    return Err(());
-                }
-                Storage::Local => Expression::Variable(id.loc, v.pos),
-            };
-
-            get_int_length(&v.ty, loc, false, ns, errors)?;
-
-            match expr {
-                pt::Expression::PostIncrement(_, _) => {
-                    // temporary to hold the value of the variable _before_ incrementing
-                    // which will be returned by the expression
-                    let temp_pos = vartab.temp(id, &v.ty);
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: temp_pos,
-                            expr: lvalue,
-                        },
-                    );
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: v.pos,
-                            expr: Expression::Add(
-                                *loc,
-                                Box::new(Expression::Variable(id.loc, v.pos)),
-                                Box::new(Expression::NumberLiteral(
-                                    *loc,
-                                    v.ty.bits(ns),
-                                    One::one(),
-                                )),
-                            ),
-                        },
-                    );
-
-                    if let Storage::Contract(n) = &v.storage {
-                        cfg.writes_contract_storage = true;
-                        cfg.add(
-                            vartab,
-                            Instr::SetStorage {
-                                ty: v.ty.clone(),
-                                local: v.pos,
-                                storage: Expression::NumberLiteral(*loc, 256, n.clone()),
-                            },
-                        );
-                    }
-
-                    Ok((Expression::Variable(id.loc, temp_pos), v.ty))
-                }
-                pt::Expression::PostDecrement(_, _) => {
-                    // temporary to hold the value of the variable _before_ decrementing
-                    // which will be returned by the expression
-                    let temp_pos = vartab.temp(id, &v.ty);
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: temp_pos,
-                            expr: lvalue,
-                        },
-                    );
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: v.pos,
-                            expr: Expression::Subtract(
-                                *loc,
-                                Box::new(Expression::Variable(id.loc, temp_pos)),
-                                Box::new(Expression::NumberLiteral(
-                                    *loc,
-                                    v.ty.bits(ns),
-                                    One::one(),
-                                )),
-                            ),
-                        },
-                    );
-
-                    if let Storage::Contract(n) = &v.storage {
-                        cfg.writes_contract_storage = true;
-                        cfg.add(
-                            vartab,
-                            Instr::SetStorage {
-                                ty: v.ty.clone(),
-                                local: v.pos,
-                                storage: Expression::NumberLiteral(*loc, 256, n.clone()),
-                            },
-                        );
-                    }
-
-                    Ok((Expression::Variable(id.loc, temp_pos), v.ty))
-                }
-                pt::Expression::PreIncrement(_, _) => {
-                    let temp_pos = vartab.temp(id, &v.ty);
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: v.pos,
-                            expr: Expression::Add(
-                                *loc,
-                                Box::new(lvalue),
-                                Box::new(Expression::NumberLiteral(
-                                    *loc,
-                                    v.ty.bits(ns),
-                                    One::one(),
-                                )),
-                            ),
-                        },
-                    );
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: temp_pos,
-                            expr: Expression::Variable(id.loc, v.pos),
-                        },
-                    );
-
-                    if let Storage::Contract(n) = &v.storage {
-                        cfg.writes_contract_storage = true;
-                        cfg.add(
-                            vartab,
-                            Instr::SetStorage {
-                                ty: v.ty.clone(),
-                                local: v.pos,
-                                storage: Expression::NumberLiteral(*loc, 256, n.clone()),
-                            },
-                        );
-                    }
-
-                    Ok((Expression::Variable(id.loc, temp_pos), v.ty))
-                }
-                pt::Expression::PreDecrement(_, _) => {
-                    let temp_pos = vartab.temp(id, &v.ty);
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: v.pos,
-                            expr: Expression::Subtract(
-                                *loc,
-                                Box::new(lvalue),
-                                Box::new(Expression::NumberLiteral(
-                                    *loc,
-                                    v.ty.bits(ns),
-                                    One::one(),
-                                )),
-                            ),
-                        },
-                    );
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: temp_pos,
-                            expr: Expression::Variable(id.loc, v.pos),
-                        },
-                    );
-
-                    if let Storage::Contract(n) = &v.storage {
-                        cfg.writes_contract_storage = true;
-                        cfg.add(
-                            vartab,
-                            Instr::SetStorage {
-                                ty: v.ty.clone(),
-                                local: v.pos,
-                                storage: Expression::NumberLiteral(*loc, 256, n.clone()),
-                            },
-                        );
-                    }
-
-                    Ok((Expression::Variable(id.loc, temp_pos), v.ty))
-                }
-                _ => unreachable!(),
-            }
-        }
-
-        // assignment
-        pt::Expression::Assign(loc, var, e) => {
-            assign(loc, var, e, cfg, contract_no, ns, vartab, errors)
-        }
-
-        pt::Expression::AssignAdd(loc, var, e)
-        | pt::Expression::AssignSubtract(loc, var, e)
-        | pt::Expression::AssignMultiply(loc, var, e)
-        | pt::Expression::AssignDivide(loc, var, e)
-        | pt::Expression::AssignModulo(loc, var, e)
-        | pt::Expression::AssignOr(loc, var, e)
-        | pt::Expression::AssignAnd(loc, var, e)
-        | pt::Expression::AssignXor(loc, var, e)
-        | pt::Expression::AssignShiftLeft(loc, var, e)
-        | pt::Expression::AssignShiftRight(loc, var, e) => {
-            assign_expr(loc, var, expr, e, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Expression::NamedFunctionCall(loc, ty, args) => {
-            if vartab.is_none() {
-                errors.push(Output::error(
-                    expr.loc(),
-                    "cannot call function in constant expression".to_string(),
-                ));
-                return Err(());
-            }
-
-            let mut blackhole = Vec::new();
-
-            match ns.resolve_type(contract_no, true, ty, &mut blackhole) {
-                Ok(resolver::Type::Struct(n)) => {
-                    return named_struct_literal(
-                        loc,
-                        n,
-                        args,
-                        cfg,
-                        contract_no,
-                        ns,
-                        vartab,
-                        errors,
-                    );
-                }
-                Ok(_) => {
-                    errors.push(Output::error(
-                        *loc,
-                        "struct or function expected".to_string(),
-                    ));
-                    return Err(());
-                }
-                _ => {}
-            }
-
-            let expr =
-                named_function_call_expr(loc, ty, args, cfg, contract_no, ns, vartab, errors)?;
-
-            let mut returns =
-                emit_function_call(expr.0, expr.1, contract_no.unwrap(), cfg, ns, vartab);
-
-            if returns.len() > 1 {
-                errors.push(Output::error(
-                    *loc,
-                    "in expression context a function cannot return more than one value"
-                        .to_string(),
-                ));
-                return Err(());
-            }
-
-            Ok(returns.remove(0))
-        }
-
-        pt::Expression::New(loc, call) => match call.as_ref() {
-            pt::Expression::FunctionCall(_, ty, args) => {
-                let (expr, expr_ty) = new(loc, ty, args, cfg, contract_no, ns, vartab, errors)?;
-
-                Ok(emit_constructor_call(expr, expr_ty, cfg, vartab))
-            }
-            pt::Expression::NamedFunctionCall(_, ty, args) => {
-                let (expr, expr_ty) =
-                    constructor_named_args(loc, ty, args, cfg, contract_no, ns, vartab, errors)?;
-
-                Ok(emit_constructor_call(expr, expr_ty, cfg, vartab))
-            }
-            _ => unreachable!(),
-        },
-        pt::Expression::Delete(loc, var) => delete(loc, var, cfg, contract_no, ns, vartab, errors),
-        pt::Expression::FunctionCall(loc, ty, args) => {
-            let mut blackhole = Vec::new();
-
-            match ns.resolve_type(contract_no, true, ty, &mut blackhole) {
-                Ok(resolver::Type::Struct(n)) => {
-                    return struct_literal(loc, n, args, cfg, contract_no, ns, vartab, errors);
-                }
-                Ok(to) => {
-                    // Cast
-                    return if args.is_empty() {
-                        errors.push(Output::error(*loc, "missing argument to cast".to_string()));
-                        Err(())
-                    } else if args.len() > 1 {
-                        errors.push(Output::error(
-                            *loc,
-                            "too many arguments to cast".to_string(),
-                        ));
-                        Err(())
-                    } else {
-                        let (expr, expr_type) =
-                            expression(&args[0], cfg, contract_no, ns, vartab, errors)?;
-
-                        Ok((cast(loc, expr, &expr_type, &to, false, ns, errors)?, to))
-                    };
-                }
-                Err(_) => {}
-            }
-
-            if vartab.is_none() {
-                errors.push(Output::error(
-                    expr.loc(),
-                    "cannot call function in constant expression".to_string(),
-                ));
-                return Err(());
-            }
-
-            let expr = function_call_expr(loc, ty, args, cfg, contract_no, ns, vartab, errors)?;
-
-            let mut returns =
-                emit_function_call(expr.0, expr.1, contract_no.unwrap(), cfg, ns, vartab);
-
-            if returns.len() > 1 {
-                errors.push(Output::error(
-                    *loc,
-                    "in expression context a function cannot return more than one value"
-                        .to_string(),
-                ));
-                return Err(());
-            }
-
-            Ok(returns.remove(0))
-        }
-        pt::Expression::ArraySubscript(loc, _, None) => {
-            errors.push(Output::error(
-                *loc,
-                "expected expression before ‘]’ token".to_string(),
-            ));
-
-            Err(())
-        }
-        pt::Expression::ArraySubscript(loc, array, Some(index)) => {
-            array_subscript(loc, array, index, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Expression::MemberAccess(loc, e, id) => {
-            member_access(loc, e, id, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Expression::Or(loc, left, right) => {
-            let boolty = resolver::Type::Bool;
-            let (l, l_type) = expression(left, cfg, contract_no, ns, vartab, errors)?;
-            let l = cast(&loc, l, &l_type, &boolty, true, ns, errors)?;
-
-            let mut tab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    // In constant context, no side effects so short-circut not necessary
-                    let (r, r_type) = expression(right, cfg, contract_no, ns, vartab, errors)?;
-
-                    return Ok((
-                        Expression::Or(
-                            *loc,
-                            Box::new(l),
-                            Box::new(cast(
-                                &loc,
-                                r,
-                                &r_type,
-                                &resolver::Type::Bool,
-                                true,
-                                ns,
-                                errors,
-                            )?),
-                        ),
-                        resolver::Type::Bool,
-                    ));
-                }
-            };
-
-            let pos = tab.temp(
-                &pt::Identifier {
-                    name: "or".to_owned(),
-                    loc: *loc,
-                },
-                &resolver::Type::Bool,
-            );
-
-            let right_side = cfg.new_basic_block("or_right_side".to_string());
-            let end_or = cfg.new_basic_block("or_end".to_string());
-
-            cfg.add(
-                tab,
-                Instr::Set {
-                    res: pos,
-                    expr: Expression::BoolLiteral(*loc, true),
-                },
-            );
-            cfg.add(
-                tab,
-                Instr::BranchCond {
-                    cond: l,
-                    true_: end_or,
-                    false_: right_side,
-                },
-            );
-            cfg.set_basic_block(right_side);
-
-            let (r, r_type) = expression(right, cfg, contract_no, ns, &mut Some(&mut tab), errors)?;
-            let r = cast(&loc, r, &r_type, &resolver::Type::Bool, true, ns, errors)?;
-
-            cfg.add(tab, Instr::Set { res: pos, expr: r });
-
-            let mut phis = HashSet::new();
-            phis.insert(pos);
-
-            cfg.set_phis(end_or, phis);
-
-            cfg.add(tab, Instr::Branch { bb: end_or });
-
-            cfg.set_basic_block(end_or);
-
-            Ok((Expression::Variable(*loc, pos), boolty))
-        }
-        pt::Expression::And(loc, left, right) => {
-            let boolty = resolver::Type::Bool;
-            let (l, l_type) = expression(left, cfg, contract_no, ns, vartab, errors)?;
-            let l = cast(&loc, l, &l_type, &boolty, true, ns, errors)?;
-
-            let mut tab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    // In constant context, no side effects so short-circut not necessary
-                    let (r, r_type) = expression(right, cfg, contract_no, ns, vartab, errors)?;
-
-                    return Ok((
-                        Expression::And(
-                            *loc,
-                            Box::new(l),
-                            Box::new(cast(
-                                &loc,
-                                r,
-                                &r_type,
-                                &resolver::Type::Bool,
-                                true,
-                                ns,
-                                errors,
-                            )?),
-                        ),
-                        resolver::Type::Bool,
-                    ));
-                }
-            };
-
-            let pos = tab.temp(
-                &pt::Identifier {
-                    name: "and".to_owned(),
-                    loc: *loc,
-                },
-                &resolver::Type::Bool,
-            );
-
-            let right_side = cfg.new_basic_block("and_right_side".to_string());
-            let end_and = cfg.new_basic_block("and_end".to_string());
-
-            cfg.add(
-                tab,
-                Instr::Set {
-                    res: pos,
-                    expr: Expression::BoolLiteral(*loc, false),
-                },
-            );
-            cfg.add(
-                tab,
-                Instr::BranchCond {
-                    cond: l,
-                    true_: right_side,
-                    false_: end_and,
-                },
-            );
-            cfg.set_basic_block(right_side);
-
-            let (r, r_type) = expression(right, cfg, contract_no, ns, &mut Some(&mut tab), errors)?;
-            let r = cast(&loc, r, &r_type, &resolver::Type::Bool, true, ns, errors)?;
-
-            cfg.add(tab, Instr::Set { res: pos, expr: r });
-
-            let mut phis = HashSet::new();
-            phis.insert(pos);
-
-            cfg.set_phis(end_and, phis);
-
-            cfg.add(tab, Instr::Branch { bb: end_and });
-
-            cfg.set_basic_block(end_and);
-
-            Ok((Expression::Variable(*loc, pos), boolty))
-        }
-        pt::Expression::Type(loc, _) => {
-            errors.push(Output::error(*loc, "type not expected".to_owned()));
-            Err(())
-        }
-        pt::Expression::List(loc, _) => {
-            errors.push(Output::error(
-                *loc,
-                "lists only permitted in destructure statements".to_owned(),
-            ));
-            Err(())
-        }
-        pt::Expression::FunctionCallBlock(loc, _, _) => {
-            errors.push(Output::error(*loc, "unexpect block encountered".to_owned()));
-            Err(())
-        }
-        pt::Expression::Unit(loc, expr, unit) => {
-            let n = match expr.as_ref() {
-                pt::Expression::NumberLiteral(_, n) => n,
-                pt::Expression::HexNumberLiteral(loc, _) => {
-                    errors.push(Output::error(
-                        *loc,
-                        "hexadecimal numbers cannot be used with unit denominations".to_owned(),
-                    ));
-                    return Err(());
-                }
-                _ => {
-                    errors.push(Output::error(
-                        *loc,
-                        "unit denominations can only be used with number literals".to_owned(),
-                    ));
-                    return Err(());
-                }
-            };
-
-            match unit {
-                pt::Unit::Wei(loc)
-                | pt::Unit::Finney(loc)
-                | pt::Unit::Szabo(loc)
-                | pt::Unit::Ether(loc)
-                    if ns.target != crate::Target::Ewasm =>
-                {
-                    errors.push(Output::warning(
-                        *loc,
-                        "ethereum currency unit used while not targetting ethereum".to_owned(),
-                    ));
-                }
-                _ => (),
-            }
-
-            bigint_to_expression(
-                loc,
-                &(n * match unit {
-                    pt::Unit::Seconds(_) => BigInt::from(1),
-                    pt::Unit::Minutes(_) => BigInt::from(60),
-                    pt::Unit::Hours(_) => BigInt::from(60 * 60),
-                    pt::Unit::Days(_) => BigInt::from(60 * 60 * 24),
-                    pt::Unit::Weeks(_) => BigInt::from(60 * 60 * 24 * 7),
-                    pt::Unit::Wei(_) => BigInt::from(1),
-                    pt::Unit::Szabo(_) => BigInt::from(10).pow(12u32),
-                    pt::Unit::Finney(_) => BigInt::from(10).pow(15u32),
-                    pt::Unit::Ether(_) => BigInt::from(10).pow(18u32),
-                }),
-                errors,
-            )
-        }
-        pt::Expression::This(loc) => match contract_no {
-            Some(contract_no) => Ok((
-                Expression::GetAddress(*loc),
-                resolver::Type::Contract(contract_no),
-            )),
-            None => {
-                errors.push(Output::warning(
-                    *loc,
-                    "this not allowed outside contract".to_owned(),
-                ));
-                Err(())
-            }
-        },
-    }
-}
-
-/// Resolve an new contract expression with positional arguments
-fn constructor(
-    loc: &pt::Loc,
-    no: usize,
-    args: &[pt::Expression],
-    call_args: CallArgs,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    // The current contract cannot be constructed with new. In order to create
-    // the contract, we need the code hash of the contract. Part of that code
-    // will be code we're emitted here. So we end up with a crypto puzzle.
-    let contract_no = match contract_no {
-        Some(n) if n == no => {
-            errors.push(Output::error(
-                *loc,
-                format!(
-                    "new cannot construct current contract ‘{}’",
-                    ns.contracts[contract_no.unwrap()].name
-                ),
-            ));
-            return Err(());
-        }
-        Some(n) => n,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                "new contract not allowed in this context".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    // check for circular references
-    if circular_reference(no, contract_no, ns) {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "circular reference creating contract ‘{}’",
-                ns.contracts[no].name
-            ),
-        ));
-        return Err(());
-    }
-
-    let mut creates = ns.contracts[contract_no].creates.borrow_mut();
-
-    if !creates.contains(&no) {
-        creates.push(no);
-    }
-
-    let mut resolved_args = Vec::new();
-    let mut resolved_types = Vec::new();
-
-    for arg in args {
-        let (expr, expr_type) = expression(arg, cfg, Some(contract_no), ns, vartab, errors)?;
-
-        resolved_args.push(Box::new(expr));
-        resolved_types.push(expr_type);
-    }
-
-    let mut temp_errors = Vec::new();
-
-    // constructor call
-    for (constructor_no, func) in ns.contracts[no]
-        .functions
-        .iter()
-        .filter(|f| f.is_constructor())
-        .enumerate()
-    {
-        if func.params.len() != args.len() {
-            temp_errors.push(Output::error(
-                *loc,
-                format!(
-                    "constructor expects {} arguments, {} provided",
-                    func.params.len(),
-                    args.len()
-                ),
-            ));
-            continue;
-        }
-
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for (i, param) in func.params.iter().enumerate() {
-            let arg = &resolved_args[i];
-
-            match cast(
-                &args[i].loc(),
-                *arg.clone(),
-                &resolved_types[i],
-                &param.ty,
-                true,
-                ns,
-                &mut temp_errors,
-            ) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                    break;
-                }
-            }
-        }
-
-        if matches {
-            return Ok((
-                Expression::Constructor {
-                    loc: *loc,
-                    contract_no: no,
-                    constructor_no,
-                    args: cast_args,
-                    value: call_args.value,
-                    gas: call_args.gas,
-                    salt: call_args.salt,
-                },
-                resolver::Type::Contract(no),
-            ));
-        }
-    }
-
-    match ns.contracts[no]
-        .functions
-        .iter()
-        .filter(|f| f.is_constructor())
-        .count()
-    {
-        0 => Ok((
-            Expression::Constructor {
-                loc: *loc,
-                contract_no: no,
-                constructor_no: 0,
-                args: Vec::new(),
-                value: call_args.value,
-                gas: call_args.gas,
-                salt: call_args.salt,
-            },
-            resolver::Type::Contract(no),
-        )),
-        1 => {
-            errors.append(&mut temp_errors);
-
-            Err(())
-        }
-        _ => {
-            errors.push(Output::error(
-                *loc,
-                "cannot find overloaded constructor which matches signature".to_string(),
-            ));
-
-            Err(())
-        }
-    }
-}
-
-/// check if from creates to, recursively
-fn circular_reference(from: usize, to: usize, ns: &resolver::Namespace) -> bool {
-    let creates = ns.contracts[from].creates.borrow();
-
-    if creates.contains(&to) {
-        return true;
-    }
-
-    creates.iter().any(|n| circular_reference(*n, to, &ns))
-}
-
-/// Resolve an new contract expression with named arguments
-pub fn constructor_named_args(
-    loc: &pt::Loc,
-    ty: &pt::Expression,
-    args: &[pt::NamedArgument],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (ty, call_args, _) = collect_call_args(ty, errors)?;
-
-    let call_args = parse_call_args(&call_args, false, cfg, contract_no, ns, vartab, errors)?;
-
-    let no = match ns.resolve_type(contract_no, false, ty, errors)? {
-        resolver::Type::Contract(n) => n,
-        _ => {
-            errors.push(Output::error(*loc, "contract expected".to_string()));
-            return Err(());
-        }
-    };
-
-    // The current contract cannot be constructed with new. In order to create
-    // the contract, we need the code hash of the contract. Part of that code
-    // will be code we're emitted here. So we end up with a crypto puzzle.
-    let contract_no = match contract_no {
-        Some(n) if n == no => {
-            errors.push(Output::error(
-                *loc,
-                format!(
-                    "new cannot construct current contract ‘{}’",
-                    ns.contracts[contract_no.unwrap()].name
-                ),
-            ));
-            return Err(());
-        }
-        Some(n) => n,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                "new contract not allowed in this context".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    // check for circular references
-    if circular_reference(no, contract_no, ns) {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "circular reference creating contract ‘{}’",
-                ns.contracts[no].name
-            ),
-        ));
-        return Err(());
-    }
-
-    let mut creates = ns.contracts[contract_no].creates.borrow_mut();
-
-    if !creates.contains(&no) {
-        creates.push(no);
-    }
-
-    let mut arguments = HashMap::new();
-
-    for arg in args {
-        arguments.insert(
-            arg.name.name.to_string(),
-            expression(&arg.expr, cfg, Some(contract_no), ns, vartab, errors)?,
-        );
-    }
-
-    let mut temp_errors = Vec::new();
-
-    // constructor call
-    for (constructor_no, func) in ns.contracts[no]
-        .functions
-        .iter()
-        .filter(|f| f.is_constructor())
-        .enumerate()
-    {
-        if func.params.len() != args.len() {
-            temp_errors.push(Output::error(
-                *loc,
-                format!(
-                    "constructor expects {} arguments, {} provided",
-                    func.params.len(),
-                    args.len()
-                ),
-            ));
-            continue;
-        }
-
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for param in func.params.iter() {
-            let arg = match arguments.get(&param.name) {
-                Some(a) => a,
-                None => {
-                    matches = false;
-                    temp_errors.push(Output::error(
-                        *loc,
-                        format!("missing argument ‘{}’ to constructor", param.name),
-                    ));
-                    break;
-                }
-            };
-
-            match cast(
-                &pt::Loc(0, 0),
-                arg.0.clone(),
-                &arg.1,
-                &param.ty,
-                true,
-                ns,
-                &mut temp_errors,
-            ) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                    break;
-                }
-            }
-        }
-
-        if matches {
-            return Ok((
-                Expression::Constructor {
-                    loc: *loc,
-                    contract_no: no,
-                    constructor_no,
-                    args: cast_args,
-                    value: call_args.value,
-                    gas: call_args.gas,
-                    salt: call_args.salt,
-                },
-                resolver::Type::Contract(no),
-            ));
-        }
-    }
-
-    match ns.contracts[no]
-        .functions
-        .iter()
-        .filter(|f| f.is_constructor())
-        .count()
-    {
-        0 => Ok((
-            Expression::Constructor {
-                loc: *loc,
-                contract_no: no,
-                constructor_no: 0,
-                args: Vec::new(),
-                value: call_args.value,
-                gas: call_args.gas,
-                salt: call_args.salt,
-            },
-            resolver::Type::Contract(no),
-        )),
-        1 => {
-            errors.append(&mut temp_errors);
-
-            Err(())
-        }
-        _ => {
-            errors.push(Output::error(
-                *loc,
-                "cannot find overloaded constructor which matches signature".to_string(),
-            ));
-
-            Err(())
-        }
-    }
-}
-
-/// Resolve type(x).foo
-pub fn type_name_expr(
-    loc: &pt::Loc,
-    args: &[pt::Expression],
-    field: &pt::Identifier,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    if args.is_empty() {
-        errors.push(Output::error(
-            *loc,
-            "missing argument to type()".to_string(),
-        ));
-        return Err(());
-    }
-
-    if args.len() > 1 {
-        errors.push(Output::error(
-            *loc,
-            format!("got {} arguments to type(), only one expected", args.len(),),
-        ));
-        return Err(());
-    }
-
-    let ty = ns.resolve_type(contract_no, false, &args[0], errors)?;
-
-    match (&ty, field.name.as_str()) {
-        (resolver::Type::Uint(_), "min") => bigint_to_expression(loc, &BigInt::zero(), errors),
-        (resolver::Type::Uint(bits), "max") => {
-            let max = BigInt::one().shl(*bits as usize).sub(1);
-            bigint_to_expression(loc, &max, errors)
-        }
-        (resolver::Type::Int(bits), "min") => {
-            let min = BigInt::zero().sub(BigInt::one().shl(*bits as usize - 1));
-            bigint_to_expression(loc, &min, errors)
-        }
-        (resolver::Type::Int(bits), "max") => {
-            let max = BigInt::one().shl(*bits as usize - 1).sub(1);
-            bigint_to_expression(loc, &max, errors)
-        }
-        (resolver::Type::Contract(n), "name") => Ok((
-            Expression::BytesLiteral(*loc, ns.contracts[*n].name.as_bytes().to_vec()),
-            resolver::Type::String,
-        )),
-        (resolver::Type::Contract(no), "creationCode")
-        | (resolver::Type::Contract(no), "runtimeCode") => {
-            let contract_no = match contract_no {
-                Some(contract_no) => contract_no,
-                None => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!(
-                            "type().{} not permitted outside of contract code",
-                            field.name
-                        ),
-                    ));
-                    return Err(());
-                }
-            };
-
-            // check for circular references
-            if *no == contract_no {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "containing our own contract code for ‘{}’ would generate infinite size contract",
-                        ns.contracts[*no].name
-                    ),
-                ));
-                return Err(());
-            }
-
-            if circular_reference(*no, contract_no, ns) {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "circular reference creating contract code for ‘{}’",
-                        ns.contracts[*no].name
-                    ),
-                ));
-                return Err(());
-            }
-
-            let mut creates = ns.contracts[contract_no].creates.borrow_mut();
-
-            if !creates.contains(no) {
-                creates.push(*no);
-            }
-
-            Ok((
-                Expression::CodeLiteral(*loc, *no, field.name == "runtimeCode"),
-                resolver::Type::DynamicBytes,
-            ))
-        }
-        _ => {
-            errors.push(Output::error(
-                *loc,
-                format!(
-                    "type ‘{}’ does not have type function {}",
-                    ty.to_string(ns),
-                    field.name
-                ),
-            ));
-            Err(())
-        }
-    }
-}
-
-/// Resolve an new expression
-pub fn new(
-    loc: &pt::Loc,
-    ty: &pt::Expression,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (ty, call_args, call_args_loc) = collect_call_args(ty, errors)?;
-
-    let ty = ns.resolve_type(contract_no, false, ty, errors)?;
-
-    match &ty {
-        resolver::Type::Array(ty, dim) => {
-            if dim.last().unwrap().is_some() {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "new cannot allocate fixed array type ‘{}’",
-                        ty.to_string(ns)
-                    ),
-                ));
-                return Err(());
-            }
-
-            if let resolver::Type::Contract(_) = ty.as_ref() {
-                errors.push(Output::error(
-                    *loc,
-                    format!("new cannot construct array of ‘{}’", ty.to_string(ns)),
-                ));
-                return Err(());
-            }
-        }
-        resolver::Type::String | resolver::Type::DynamicBytes => {}
-        resolver::Type::Contract(n) => {
-            let call_args =
-                parse_call_args(&call_args, false, cfg, contract_no, ns, vartab, errors)?;
-
-            return constructor(
-                loc,
-                *n,
-                args,
-                call_args,
-                cfg,
-                contract_no,
-                ns,
-                vartab,
-                errors,
-            );
-        }
-        _ => {
-            errors.push(Output::error(
-                *loc,
-                format!("new cannot allocate type ‘{}’", ty.to_string(ns)),
-            ));
-            return Err(());
-        }
-    };
-
-    if let Some(loc) = call_args_loc {
-        errors.push(Output::error(
-            loc,
-            "constructor arguments not permitted for allocation".to_string(),
-        ));
-        return Err(());
-    }
-
-    if args.len() != 1 {
-        errors.push(Output::error(
-            *loc,
-            "new dynamic array should have a single length argument".to_string(),
-        ));
-        return Err(());
-    }
-    let size_loc = args[0].loc();
-
-    let (size_expr, size_ty) = expression(&args[0], cfg, contract_no, ns, vartab, errors)?;
-
-    let size_width = match size_ty {
-        resolver::Type::Uint(n) => n,
-        _ => {
-            errors.push(Output::error(
-                size_loc,
-                format!(
-                    "new size argument must be unsigned integer, not ‘{}’",
-                    size_ty.to_string(ns)
-                ),
-            ));
-            return Err(());
-        }
-    };
-
-    // TODO: should we check an upper bound? Large allocations will fail anyway,
-    // and ethereum solidity does not check at compile time
-    let size = match size_width.cmp(&32) {
-        Ordering::Greater => {
-            Expression::Trunc(size_loc, resolver::Type::Uint(32), Box::new(size_expr))
-        }
-        Ordering::Less => {
-            Expression::ZeroExt(size_loc, resolver::Type::Uint(32), Box::new(size_expr))
-        }
-        Ordering::Equal => size_expr,
-    };
-
-    Ok((
-        Expression::AllocDynamicArray(*loc, ty.clone(), Box::new(size), None),
-        ty,
-    ))
-}
-
-/// Test for equality; first check string equality, then integer equality
-fn equal(
-    loc: &pt::Loc,
-    l: &pt::Expression,
-    r: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<Expression, ()> {
-    let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-    let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-    // Comparing stringliteral against stringliteral
-    if let (Expression::BytesLiteral(_, l), Expression::BytesLiteral(_, r)) = (&left, &right) {
-        return Ok(Expression::BoolLiteral(*loc, l == r));
-    }
-
-    // compare string against literal
-    match (&left, &right_type.deref()) {
-        (Expression::BytesLiteral(_, l), resolver::Type::String)
-        | (Expression::BytesLiteral(_, l), resolver::Type::DynamicBytes) => {
-            return Ok(Expression::StringCompare(
-                *loc,
-                StringLocation::RunTime(Box::new(cast(
-                    &r.loc(),
-                    right,
-                    &right_type,
-                    &right_type.deref(),
-                    true,
-                    ns,
-                    errors,
-                )?)),
-                StringLocation::CompileTime(l.clone()),
-            ));
-        }
-        _ => {}
-    }
-
-    match (&right, &left_type.deref()) {
-        (Expression::BytesLiteral(_, literal), resolver::Type::String)
-        | (Expression::BytesLiteral(_, literal), resolver::Type::DynamicBytes) => {
-            return Ok(Expression::StringCompare(
-                *loc,
-                StringLocation::RunTime(Box::new(cast(
-                    &l.loc(),
-                    left,
-                    &left_type,
-                    &left_type.deref(),
-                    true,
-                    ns,
-                    errors,
-                )?)),
-                StringLocation::CompileTime(literal.clone()),
-            ));
-        }
-        _ => {}
-    }
-
-    // compare string
-    match (&left_type.deref(), &right_type.deref()) {
-        (resolver::Type::String, resolver::Type::String)
-        | (resolver::Type::DynamicBytes, resolver::Type::DynamicBytes) => {
-            return Ok(Expression::StringCompare(
-                *loc,
-                StringLocation::RunTime(Box::new(cast(
-                    &l.loc(),
-                    left,
-                    &left_type,
-                    &left_type.deref(),
-                    true,
-                    ns,
-                    errors,
-                )?)),
-                StringLocation::RunTime(Box::new(cast(
-                    &r.loc(),
-                    right,
-                    &right_type,
-                    &right_type.deref(),
-                    true,
-                    ns,
-                    errors,
-                )?)),
-            ));
-        }
-        _ => {}
-    }
-
-    let ty = coerce(&left_type, &l.loc(), &right_type, &r.loc(), ns, errors)?;
-
-    Ok(Expression::Equal(
-        *loc,
-        Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-        Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-    ))
-}
-
-/// Try string concatenation
-fn addition(
-    loc: &pt::Loc,
-    l: &pt::Expression,
-    r: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (left, left_type) = expression(l, cfg, contract_no, ns, vartab, errors)?;
-    let (right, right_type) = expression(r, cfg, contract_no, ns, vartab, errors)?;
-
-    // Concatenate stringliteral with stringliteral
-    if let (Expression::BytesLiteral(_, l), Expression::BytesLiteral(_, r)) = (&left, &right) {
-        let mut c = Vec::with_capacity(l.len() + r.len());
-        c.extend_from_slice(l);
-        c.extend_from_slice(r);
-        let length = c.len();
-        return Ok((
-            Expression::BytesLiteral(*loc, c),
-            resolver::Type::Bytes(length as u8),
-        ));
-    }
-
-    // compare string against literal
-    match (&left, &right_type) {
-        (Expression::BytesLiteral(_, l), resolver::Type::String)
-        | (Expression::BytesLiteral(_, l), resolver::Type::DynamicBytes) => {
-            return Ok((
-                Expression::StringConcat(
-                    *loc,
-                    StringLocation::CompileTime(l.clone()),
-                    StringLocation::RunTime(Box::new(right)),
-                ),
-                right_type,
-            ));
-        }
-        _ => {}
-    }
-
-    match (&right, &left_type) {
-        (Expression::BytesLiteral(_, l), resolver::Type::String)
-        | (Expression::BytesLiteral(_, l), resolver::Type::DynamicBytes) => {
-            return Ok((
-                Expression::StringConcat(
-                    *loc,
-                    StringLocation::RunTime(Box::new(left)),
-                    StringLocation::CompileTime(l.clone()),
-                ),
-                left_type,
-            ));
-        }
-        _ => {}
-    }
-
-    // compare string
-    match (&left_type, &right_type) {
-        (resolver::Type::String, resolver::Type::String)
-        | (resolver::Type::DynamicBytes, resolver::Type::DynamicBytes) => {
-            return Ok((
-                Expression::StringConcat(
-                    *loc,
-                    StringLocation::RunTime(Box::new(left)),
-                    StringLocation::RunTime(Box::new(right)),
-                ),
-                right_type,
-            ));
-        }
-        _ => {}
-    }
-
-    let ty = coerce_int(
-        &left_type,
-        &l.loc(),
-        &right_type,
-        &r.loc(),
-        false,
-        ns,
-        errors,
-    )?;
-
-    Ok((
-        Expression::Add(
-            *loc,
-            Box::new(cast(&l.loc(), left, &left_type, &ty, true, ns, errors)?),
-            Box::new(cast(&r.loc(), right, &right_type, &ty, true, ns, errors)?),
-        ),
-        ty,
-    ))
-}
-
-/// Resolve an assignment
-fn assign(
-    loc: &pt::Loc,
-    var: &pt::Expression,
-    e: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    // is it a destructuring assignment
-    if let pt::Expression::List(_, var) = var {
-        destructuring(loc, var, e, cfg, contract_no, ns, vartab, errors)
-    } else {
-        let (expr, expr_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-        assign_single(
-            loc,
-            var,
-            expr,
-            expr_type,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            errors,
-        )
-    }
-}
-
-/// Resolve an assignment
-fn assign_single(
-    loc: &pt::Loc,
-    var: &pt::Expression,
-    expr: Expression,
-    expr_type: resolver::Type,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    match var {
-        pt::Expression::Variable(id) => {
-            let vartab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!(
-                            "cannot access variable ‘{}’ in constant expression",
-                            id.name
-                        ),
-                    ));
-                    return Err(());
-                }
-            };
-            let var = vartab.find(id, contract_no.unwrap(), ns, errors)?;
-
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    res: var.pos,
-                    expr: cast(&id.loc, expr, &expr_type, &var.ty, true, ns, errors)?,
-                },
-            );
-
-            match &var.storage {
-                Storage::Contract(n) => {
-                    cfg.writes_contract_storage = true;
-                    cfg.add(
-                        vartab,
-                        Instr::SetStorage {
-                            ty: var.ty.clone(),
-                            local: var.pos,
-                            storage: Expression::NumberLiteral(*loc, 256, n.clone()),
-                        },
-                    );
-                }
-                Storage::Constant(_) => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("cannot assign to constant ‘{}’", id.name),
-                    ));
-                    return Err(());
-                }
-                Storage::Local => {
-                    // nothing to do
-                }
-            }
-
-            Ok((Expression::Variable(id.loc, var.pos), var.ty))
-        }
-        _ => {
-            // for example: a[0] = 102
-            let (var_expr, var_ty) = expression(var, cfg, contract_no, ns, vartab, errors)?;
-
-            let vartab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    errors.push(Output::error(
-                        *loc,
-                        "cannot assign in constant expression".to_string(),
-                    ));
-                    return Err(());
-                }
-            };
-
-            match &var_ty {
-                resolver::Type::Ref(r_ty) => {
-                    let pos = vartab.temp_anonymous(&var_ty);
-
-                    // reference to memory (e.g. array)
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: pos,
-                            expr: cast(&var.loc(), expr, &expr_type, &r_ty, true, ns, errors)?,
-                        },
-                    );
-
-                    // set the element in memory
-                    cfg.add(
-                        vartab,
-                        Instr::Store {
-                            dest: var_expr,
-                            pos,
-                        },
-                    );
-
-                    Ok((Expression::Variable(*loc, pos), r_ty.as_ref().clone()))
-                }
-                resolver::Type::StorageRef(r_ty) => {
-                    let pos = vartab.temp_anonymous(&r_ty);
-
-                    cfg.add(
-                        vartab,
-                        Instr::Set {
-                            res: pos,
-                            expr: cast(&var.loc(), expr, &expr_type, &r_ty, true, ns, errors)?,
-                        },
-                    );
-
-                    if let Expression::StorageBytesSubscript(_, array, index) = var_expr {
-                        // Set a byte in a byte array
-                        cfg.add(
-                            vartab,
-                            Instr::SetStorageBytes {
-                                local: pos,
-                                storage: array,
-                                offset: index,
-                            },
-                        );
-                    } else {
-                        // The value of the var_expr should be storage offset
-                        cfg.add(
-                            vartab,
-                            Instr::SetStorage {
-                                ty: *r_ty.clone(),
-                                local: pos,
-                                storage: var_expr,
-                            },
-                        );
-                    }
-                    cfg.writes_contract_storage = true;
-
-                    Ok((Expression::Variable(*loc, pos), r_ty.as_ref().clone()))
-                }
-                _ => {
-                    errors.push(Output::error(
-                        var.loc(),
-                        "expression is not assignable".to_string(),
-                    ));
-                    Err(())
-                }
-            }
-        }
-    }
-}
-
-/// Resolve an destructuring assignment
-fn destructuring(
-    loc: &pt::Loc,
-    var: &[(pt::Loc, Option<pt::Parameter>)],
-    e: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let vartab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                "assignment not allowed in constant context".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    let mut args = match e {
-        pt::Expression::FunctionCall(loc, ty, args) => {
-            let expr = function_call_expr(
-                loc,
-                ty,
-                args,
-                cfg,
-                contract_no,
-                ns,
-                &mut Some(vartab),
-                errors,
-            )?;
-
-            emit_function_call(
-                expr.0,
-                expr.1,
-                contract_no.unwrap(),
-                cfg,
-                ns,
-                &mut Some(vartab),
-            )
-        }
-        pt::Expression::NamedFunctionCall(loc, ty, args) => {
-            let expr = named_function_call_expr(
-                loc,
-                ty,
-                args,
-                cfg,
-                contract_no,
-                ns,
-                &mut Some(vartab),
-                errors,
-            )?;
-
-            emit_function_call(
-                expr.0,
-                expr.1,
-                contract_no.unwrap(),
-                cfg,
-                ns,
-                &mut Some(vartab),
-            )
-        }
-        _ => {
-            let mut list = Vec::new();
-
-            for e in parameter_list_to_expr_list(e, errors)? {
-                let (expr, ty) = expression(e, cfg, contract_no, ns, &mut Some(vartab), errors)?;
-
-                // we need to copy the arguments into temps in case there is a swap involved
-                // we're assuming that llvm will optimize these away if possible
-                let pos = vartab.temp_anonymous(&ty);
-                cfg.add(vartab, Instr::Set { res: pos, expr });
-                list.push((Expression::Variable(e.loc(), pos), ty));
-            }
-
-            list
-        }
-    };
-
-    if args.len() != var.len() {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "destructuring assignment has {} values on the left and {} on the right",
-                var.len(),
-                args.len()
-            ),
-        ));
-        return Err(());
-    }
-
-    for e in var {
-        let (arg, arg_ty) = args.remove(0);
-
-        match &e.1 {
-            None => {
-                // nothing to do
-            }
-            Some(pt::Parameter {
-                ty,
-                storage,
-                name: None,
-            }) => {
-                // so this is a simple assignment, e.g. "(foo, bar) = (1, 2);"
-                // both foo and bar should be declared
-                assign_single(
-                    &e.0,
-                    &ty,
-                    arg,
-                    arg_ty,
-                    cfg,
-                    contract_no,
-                    ns,
-                    &mut Some(vartab),
-                    errors,
-                )?;
-
-                if let Some(storage) = storage {
-                    errors.push(Output::error(
-                        *storage.loc(),
-                        format!("storage modifier ‘{}’ not permitted on assignment", storage),
-                    ));
-                    return Err(());
-                }
-            }
-            Some(pt::Parameter {
-                ty,
-                storage,
-                name: Some(name),
-            }) => {
-                let var_ty = resolve_var_decl_ty(&ty, &storage, contract_no, ns, errors)?;
-
-                let expr = cast(&e.0, arg, &arg_ty, &var_ty, true, ns, errors)?;
-
-                if let Some(pos) = vartab.add(&name, var_ty, errors) {
-                    ns.check_shadowing(contract_no.unwrap(), &name, errors);
-
-                    cfg.add(vartab, Instr::Set { res: pos, expr });
-                }
-            }
-        }
-    }
-
-    Ok((Expression::Poison, resolver::Type::Undef))
-}
-
-/// Resolve an assignment with an operator
-fn assign_expr(
-    loc: &pt::Loc,
-    var: &pt::Expression,
-    expr: &pt::Expression,
-    e: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (set, set_type) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-    let op = |assign: Expression,
-              ty: &resolver::Type,
-              errors: &mut Vec<output::Output>|
-     -> Result<Expression, ()> {
-        let set = match expr {
-            pt::Expression::AssignShiftLeft(_, _, _)
-            | pt::Expression::AssignShiftRight(_, _, _) => {
-                let left_length = get_int_length(&ty, &loc, true, ns, errors)?;
-                let right_length = get_int_length(&set_type, &e.loc(), false, ns, errors)?;
-
-                // TODO: does shifting by negative value need compiletime/runtime check?
-                if left_length == right_length {
-                    set
-                } else if right_length < left_length && set_type.signed() {
-                    Expression::SignExt(*loc, ty.clone(), Box::new(set))
-                } else if right_length < left_length && !set_type.signed() {
-                    Expression::ZeroExt(*loc, ty.clone(), Box::new(set))
-                } else {
-                    Expression::Trunc(*loc, ty.clone(), Box::new(set))
-                }
-            }
-            _ => cast(&var.loc(), set, &set_type, &ty, true, ns, errors)?,
-        };
-
-        Ok(match expr {
-            pt::Expression::AssignAdd(_, _, _) => {
-                Expression::Add(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignSubtract(_, _, _) => {
-                Expression::Subtract(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignMultiply(_, _, _) => {
-                Expression::Multiply(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignOr(_, _, _) => {
-                Expression::BitwiseOr(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignAnd(_, _, _) => {
-                Expression::BitwiseAnd(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignXor(_, _, _) => {
-                Expression::BitwiseXor(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignShiftLeft(_, _, _) => {
-                Expression::ShiftLeft(*loc, Box::new(assign), Box::new(set))
-            }
-            pt::Expression::AssignShiftRight(_, _, _) => {
-                Expression::ShiftRight(*loc, Box::new(assign), Box::new(set), ty.signed())
-            }
-            pt::Expression::AssignDivide(_, _, _) => {
-                if ty.signed() {
-                    Expression::SDivide(*loc, Box::new(assign), Box::new(set))
-                } else {
-                    Expression::UDivide(*loc, Box::new(assign), Box::new(set))
-                }
-            }
-            pt::Expression::AssignModulo(_, _, _) => {
-                if ty.signed() {
-                    Expression::SModulo(*loc, Box::new(assign), Box::new(set))
-                } else {
-                    Expression::UModulo(*loc, Box::new(assign), Box::new(set))
-                }
-            }
-            _ => unreachable!(),
-        })
-    };
-
-    // either it's a variable, or a reference to an array element
-    match var {
-        pt::Expression::Variable(id) => {
-            let tab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    errors.push(Output::error(
-                        *loc,
-                        "cannot assign in constant expression".to_string(),
-                    ));
-                    return Err(());
-                }
-            };
-
-            let v = tab.find(id, contract_no.unwrap(), ns, errors)?;
-
-            match v.ty {
-                resolver::Type::Bytes(_) | resolver::Type::Int(_) | resolver::Type::Uint(_) => (),
-                _ => {
-                    errors.push(Output::error(
-                        var.loc(),
-                        format!(
-                            "variable ‘{}’ of incorrect type {}",
-                            id.name.to_string(),
-                            v.ty.to_string(ns)
-                        ),
-                    ));
-                    return Err(());
-                }
-            };
-
-            let lvalue = match &v.storage {
-                Storage::Contract(n) => Expression::StorageLoad(
-                    *loc,
-                    v.ty.clone(),
-                    Box::new(Expression::NumberLiteral(*loc, 256, n.clone())),
-                ),
-                Storage::Constant(_) => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("cannot assign to constant ‘{}’", id.name),
-                    ));
-                    return Err(());
-                }
-                Storage::Local => Expression::Variable(id.loc, v.pos),
-            };
-
-            let set = op(lvalue, &v.ty, errors)?;
-
-            cfg.add(
-                tab,
-                Instr::Set {
-                    res: v.pos,
-                    expr: set,
-                },
-            );
-
-            match &v.storage {
-                Storage::Contract(n) => {
-                    cfg.writes_contract_storage = true;
-                    cfg.add(
-                        tab,
-                        Instr::SetStorage {
-                            ty: v.ty.clone(),
-                            local: v.pos,
-                            storage: Expression::NumberLiteral(*loc, 256, n.clone()),
-                        },
-                    );
-                }
-                Storage::Constant(_) => {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("cannot assign to constant ‘{}’", id.name),
-                    ));
-                    return Err(());
-                }
-                Storage::Local => {
-                    // nothing to do
-                }
-            }
-
-            Ok((Expression::Variable(id.loc, v.pos), v.ty))
-        }
-        _ => {
-            let (var_expr, var_ty) = expression(var, cfg, contract_no, ns, vartab, errors)?;
-
-            let tab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => {
-                    errors.push(Output::error(
-                        *loc,
-                        "cannot assign in constant expression".to_string(),
-                    ));
-                    return Err(());
-                }
-            };
-            let pos = tab.temp_anonymous(&var_ty);
-
-            match var_ty {
-                resolver::Type::Ref(ref r_ty) => match r_ty.as_ref() {
-                    resolver::Type::Bytes(_) | resolver::Type::Int(_) | resolver::Type::Uint(_) => {
-                        let set = op(
-                            cast(
-                                loc,
-                                var_expr.clone(),
-                                &var_ty,
-                                r_ty.as_ref(),
-                                true,
-                                ns,
-                                errors,
-                            )?,
-                            &*r_ty,
-                            errors,
-                        )?;
-
-                        cfg.add(
-                            tab,
-                            Instr::Set {
-                                res: pos,
-                                expr: set,
-                            },
-                        );
-                        cfg.add(
-                            tab,
-                            Instr::Store {
-                                dest: var_expr,
-                                pos,
-                            },
-                        );
-                        Ok((Expression::Variable(*loc, pos), r_ty.as_ref().clone()))
-                    }
-                    _ => {
-                        errors.push(Output::error(
-                            var.loc(),
-                            format!("assigning to incorrect type {}", r_ty.to_string(ns)),
-                        ));
-                        Err(())
-                    }
-                },
-                resolver::Type::StorageRef(ref r_ty) => match r_ty.as_ref() {
-                    resolver::Type::Bytes(_) | resolver::Type::Int(_) | resolver::Type::Uint(_) => {
-                        let set = op(
-                            cast(
-                                loc,
-                                var_expr.clone(),
-                                &var_ty,
-                                r_ty.as_ref(),
-                                true,
-                                ns,
-                                errors,
-                            )?,
-                            &*r_ty,
-                            errors,
-                        )?;
-
-                        cfg.add(
-                            tab,
-                            Instr::Set {
-                                res: pos,
-                                expr: set,
-                            },
-                        );
-
-                        if let Expression::StorageBytesSubscript(_, array, index) = var_expr {
-                            // Set a byte in a byte array
-                            cfg.add(
-                                tab,
-                                Instr::SetStorageBytes {
-                                    local: pos,
-                                    storage: array,
-                                    offset: index,
-                                },
-                            );
-                        } else {
-                            // The value of the var_expr should be storage offset
-                            cfg.add(
-                                tab,
-                                Instr::SetStorage {
-                                    ty: *r_ty.clone(),
-                                    local: pos,
-                                    storage: var_expr,
-                                },
-                            );
-                        }
-                        cfg.writes_contract_storage = true;
-                        Ok((Expression::Variable(*loc, pos), r_ty.as_ref().clone()))
-                    }
-                    _ => {
-                        errors.push(Output::error(
-                            var.loc(),
-                            format!("assigning to incorrect type {}", r_ty.to_string(ns)),
-                        ));
-                        Err(())
-                    }
-                },
-                _ => {
-                    errors.push(Output::error(
-                        var.loc(),
-                        "expression is not assignable".to_string(),
-                    ));
-                    Err(())
-                }
-            }
-        }
-    }
-}
-
-/// Resolve an array subscript expression
-fn member_access(
-    loc: &pt::Loc,
-    e: &pt::Expression,
-    id: &pt::Identifier,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    // is of the form "contract_name.enum_name.enum_value"
-    if let pt::Expression::MemberAccess(_, e, enum_name) = e {
-        if let pt::Expression::Variable(contract_name) = e.as_ref() {
-            if let Some(contract_no) = ns.resolve_contract(contract_name) {
-                if let Some(e) = ns.resolve_enum(Some(contract_no), enum_name) {
-                    return match ns.enums[e].values.get(&id.name) {
-                        Some((_, val)) => Ok((
-                            Expression::NumberLiteral(
-                                *loc,
-                                ns.enums[e].ty.bits(ns),
-                                BigInt::from_usize(*val).unwrap(),
-                            ),
-                            resolver::Type::Enum(e),
-                        )),
-                        None => {
-                            errors.push(Output::error(
-                                id.loc,
-                                format!(
-                                    "enum {} does not have value {}",
-                                    ns.enums[e].print_to_string(),
-                                    id.name
-                                ),
-                            ));
-                            Err(())
-                        }
-                    };
-                }
-            }
-        }
-    }
-
-    // is of the form "enum_name.enum_value"
-    if let pt::Expression::Variable(namespace) = e {
-        if let Some(e) = ns.resolve_enum(contract_no, namespace) {
-            return match ns.enums[e].values.get(&id.name) {
-                Some((_, val)) => Ok((
-                    Expression::NumberLiteral(
-                        *loc,
-                        ns.enums[e].ty.bits(ns),
-                        BigInt::from_usize(*val).unwrap(),
-                    ),
-                    resolver::Type::Enum(e),
-                )),
-                None => {
-                    errors.push(Output::error(
-                        id.loc,
-                        format!(
-                            "enum {} does not have value {}",
-                            ns.enums[e].print_to_string(),
-                            id.name
-                        ),
-                    ));
-                    Err(())
-                }
-            };
-        }
-    }
-
-    // is of the form "type(x).field", like type(c).min
-    if let pt::Expression::FunctionCall(_, name, args) = e {
-        if let pt::Expression::Variable(func_name) = name.as_ref() {
-            if func_name.name == "type" {
-                return type_name_expr(loc, args, id, contract_no, ns, errors);
-            }
-        }
-    }
-
-    let (expr, expr_ty) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-    // Dereference if need to. This could be struct-in-struct for
-    // example.
-    let (expr, expr_ty) = if let resolver::Type::Ref(ty) = expr_ty {
-        (Expression::Load(*loc, Box::new(expr)), *ty)
-    } else {
-        (expr, expr_ty)
-    };
-
-    match expr_ty {
-        resolver::Type::Bytes(n) => {
-            if id.name == "length" {
-                return Ok((
-                    Expression::NumberLiteral(*loc, 8, BigInt::from_u8(n).unwrap()),
-                    resolver::Type::Uint(8),
-                ));
-            }
-        }
-        resolver::Type::Array(_, dim) => {
-            if id.name == "length" {
-                return match dim.last().unwrap() {
-                    None => Ok((
-                        Expression::DynamicArrayLength(*loc, Box::new(expr)),
-                        resolver::Type::Uint(32),
-                    )),
-                    Some(d) => bigint_to_expression(loc, d, errors),
-                };
-            }
-        }
-        resolver::Type::String | resolver::Type::DynamicBytes => {
-            if id.name == "length" {
-                return Ok((
-                    Expression::DynamicArrayLength(*loc, Box::new(expr)),
-                    resolver::Type::Uint(32),
-                ));
-            }
-        }
-        resolver::Type::StorageRef(r) => match *r {
-            resolver::Type::Struct(n) => {
-                let mut slot = BigInt::zero();
-
-                for field in &ns.structs[n].fields {
-                    if id.name == field.name {
-                        return Ok((
-                            Expression::Add(
-                                *loc,
-                                Box::new(expr),
-                                Box::new(Expression::NumberLiteral(*loc, 256, slot)),
-                            ),
-                            resolver::Type::StorageRef(Box::new(field.ty.clone())),
-                        ));
-                    }
-
-                    slot += field.ty.storage_slots(ns);
-                }
-
-                errors.push(Output::error(
-                    id.loc,
-                    format!(
-                        "struct ‘{}’ does not have a field called ‘{}’",
-                        ns.structs[n].name, id.name
-                    ),
-                ));
-                return Err(());
-            }
-            resolver::Type::Bytes(n) => {
-                if id.name == "length" {
-                    return Ok((
-                        Expression::NumberLiteral(*loc, 8, BigInt::from_u8(n).unwrap()),
-                        resolver::Type::Uint(8),
-                    ));
-                }
-            }
-            resolver::Type::Array(_, dim) => {
-                if id.name == "length" {
-                    return match dim.last().unwrap() {
-                        None => Ok((
-                            expr,
-                            resolver::Type::StorageRef(Box::new(resolver::Type::Uint(256))),
-                        )),
-                        Some(d) => bigint_to_expression(loc, d, errors),
-                    };
-                }
-            }
-            resolver::Type::DynamicBytes => {
-                if id.name == "length" {
-                    return Ok((
-                        Expression::StorageBytesLength(*loc, Box::new(expr)),
-                        resolver::Type::Uint(32),
-                    ));
-                }
-            }
-            _ => {}
-        },
-        resolver::Type::Struct(n) => {
-            if let Some((i, f)) = ns.structs[n]
-                .fields
-                .iter()
-                .enumerate()
-                .find(|f| id.name == f.1.name)
-            {
-                return Ok((
-                    Expression::StructMember(*loc, Box::new(expr), i),
-                    resolver::Type::Ref(Box::new(f.ty.clone())),
-                ));
-            } else {
-                errors.push(Output::error(
-                    id.loc,
-                    format!(
-                        "struct ‘{}’ does not have a field called ‘{}’",
-                        ns.structs[n].print_to_string(),
-                        id.name
-                    ),
-                ));
-                return Err(());
-            }
-        }
-        resolver::Type::Address(_) => {
-            if id.name == "balance" {
-                if ns.target == crate::Target::Substrate {
-                    if let Expression::GetAddress(_) = expr {
-                        //
-                    } else {
-                        errors.push(Output::error(
-                                    expr.loc(),
-                                        "substrate can only retrieve balance of this, like ‘address(this).balance’".to_string(),
-                                ));
-                        return Err(());
-                    }
-                }
-
-                return Ok((
-                    Expression::Balance(*loc, Box::new(expr)),
-                    resolver::Type::Uint(ns.value_length as u16 * 8),
-                ));
-            }
-        }
-        _ => (),
-    }
-
-    errors.push(Output::error(*loc, format!("‘{}’ not found", id.name)));
-
-    Err(())
-}
-
-/// Resolve an array subscript expression
-fn array_subscript(
-    loc: &pt::Loc,
-    array: &pt::Expression,
-    index: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (mut array_expr, array_ty) = expression(array, cfg, contract_no, ns, vartab, errors)?;
-
-    if array_ty.is_mapping() {
-        return mapping_subscript(
-            loc,
-            array_expr,
-            &array_ty,
-            index,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            errors,
-        );
-    }
-
-    let (index_expr, index_ty) = expression(index, cfg, contract_no, ns, vartab, errors)?;
-
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                "cannot read subscript in constant expression".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    let index_width = match index_ty {
-        resolver::Type::Uint(w) => w,
-        _ => {
-            errors.push(Output::error(
-                *loc,
-                format!(
-                    "array subscript must be an unsigned integer, not ‘{}’",
-                    index_ty.to_string(ns)
-                ),
-            ));
-            return Err(());
-        }
-    };
-
-    if array_ty.is_storage_bytes() {
-        return Ok((
-            Expression::StorageBytesSubscript(
-                *loc,
-                Box::new(array_expr),
-                Box::new(cast(
-                    &index.loc(),
-                    index_expr,
-                    &index_ty,
-                    &resolver::Type::Uint(32),
-                    false,
-                    ns,
-                    errors,
-                )?),
-            ),
-            resolver::Type::StorageRef(Box::new(resolver::Type::Bytes(1))),
-        ));
-    }
-
-    let (array_length, array_length_ty) = match array_ty.deref() {
-        resolver::Type::Bytes(n) => bigint_to_expression(loc, &BigInt::from(*n), errors)?,
-        resolver::Type::Array(_, _) => match array_ty.array_length() {
-            None => {
-                if let resolver::Type::StorageRef(_) = array_ty {
-                    let array_length = Expression::StorageLoad(
-                        *loc,
-                        resolver::Type::Uint(256),
-                        Box::new(array_expr.clone()),
-                    );
-
-                    let slot_ty = resolver::Type::Uint(256);
-
-                    array_expr = Expression::Keccak256(*loc, vec![(array_expr, slot_ty)]);
-
-                    (array_length, resolver::Type::Uint(256))
-                } else {
-                    (
-                        Expression::DynamicArrayLength(
-                            *loc,
-                            Box::new(cast(
-                                &array.loc(),
-                                array_expr.clone(),
-                                &array_ty,
-                                &array_ty.deref(),
-                                true,
-                                ns,
-                                errors,
-                            )?),
-                        ),
-                        resolver::Type::Uint(32),
-                    )
-                }
-            }
-            Some(l) => bigint_to_expression(loc, l, errors)?,
-        },
-        resolver::Type::String => {
-            errors.push(Output::error(
-                array.loc(),
-                "array subscript is not permitted on string".to_string(),
-            ));
-            return Err(());
-        }
-        resolver::Type::DynamicBytes => (
-            // FIXME does not handle bytes in storage
-            Expression::DynamicArrayLength(*loc, Box::new(array_expr.clone())),
-            resolver::Type::Uint(32),
-        ),
-        _ => {
-            errors.push(Output::error(
-                array.loc(),
-                "expression is not an array".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    let array_width = array_length_ty.bits(ns);
-    let width = std::cmp::max(array_width, index_width);
-    let coerced_ty = resolver::Type::Uint(width);
-
-    let pos = tab.temp(
-        &pt::Identifier {
-            name: "index".to_owned(),
-            loc: *loc,
-        },
-        &coerced_ty,
-    );
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: pos,
-            expr: cast(
-                &index.loc(),
-                index_expr,
-                &index_ty,
-                &coerced_ty,
-                false,
-                ns,
-                errors,
-            )?,
-        },
-    );
-
-    // If the array is fixed length and the index also constant, the
-    // branch will be optimized away.
-    let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
-    let in_bounds = cfg.new_basic_block("in_bounds".to_string());
-
-    cfg.add(
-        tab,
-        Instr::BranchCond {
-            cond: Expression::UMoreEqual(
-                *loc,
-                Box::new(Expression::Variable(index.loc(), pos)),
-                Box::new(cast(
-                    &array.loc(),
-                    array_length.clone(),
-                    &array_length_ty,
-                    &coerced_ty,
-                    false,
-                    ns,
-                    errors,
-                )?),
-            ),
-            true_: out_of_bounds,
-            false_: in_bounds,
-        },
-    );
-
-    cfg.set_basic_block(out_of_bounds);
-    cfg.add(tab, Instr::AssertFailure { expr: None });
-
-    cfg.set_basic_block(in_bounds);
-
-    if let resolver::Type::StorageRef(ty) = array_ty {
-        let elem_ty = ty.storage_deref();
-        let elem_size = elem_ty.storage_slots(ns);
-        let mut nullsink = Vec::new();
-
-        if let Ok(array_length) = eval_number_expression(&array_length, &mut nullsink) {
-            if array_length.1.mul(elem_size.clone()).to_u64().is_some() {
-                // we need to calculate the storage offset. If this can be done with 64 bit
-                // arithmetic it will be much more efficient on wasm
-                return Ok((
-                    Expression::Add(
-                        *loc,
-                        Box::new(array_expr),
-                        Box::new(Expression::ZeroExt(
-                            *loc,
-                            resolver::Type::Uint(256),
-                            Box::new(Expression::Multiply(
-                                *loc,
-                                Box::new(cast(
-                                    &index.loc(),
-                                    Expression::Variable(index.loc(), pos),
-                                    &coerced_ty,
-                                    &resolver::Type::Uint(64),
-                                    false,
-                                    ns,
-                                    errors,
-                                )?),
-                                Box::new(Expression::NumberLiteral(*loc, 64, elem_size)),
-                            )),
-                        )),
-                    ),
-                    elem_ty,
-                ));
-            }
-        }
-
-        Ok((
-            array_offset(
-                loc,
-                array_expr,
-                cast(
-                    &index.loc(),
-                    Expression::Variable(index.loc(), pos),
-                    &coerced_ty,
-                    &resolver::Type::Uint(256),
-                    false,
-                    ns,
-                    errors,
-                )?,
-                elem_ty.clone(),
-                ns,
-            ),
-            elem_ty,
-        ))
-    } else {
-        match array_ty.deref() {
-            resolver::Type::Bytes(array_length) => {
-                let res_ty = resolver::Type::Bytes(1);
-
-                Ok((
-                    Expression::Trunc(
-                        *loc,
-                        res_ty.clone(),
-                        Box::new(Expression::ShiftRight(
-                            *loc,
-                            Box::new(array_expr),
-                            // shift by (array_length - 1 - index) * 8
-                            Box::new(Expression::ShiftLeft(
-                                *loc,
-                                Box::new(Expression::Subtract(
-                                    *loc,
-                                    Box::new(Expression::NumberLiteral(
-                                        *loc,
-                                        *array_length as u16 * 8,
-                                        BigInt::from_u8(array_length - 1).unwrap(),
-                                    )),
-                                    Box::new(cast_shift_arg(
-                                        loc,
-                                        Expression::Variable(index.loc(), pos),
-                                        index_width,
-                                        &array_ty,
-                                        ns,
-                                    )),
-                                )),
-                                Box::new(Expression::NumberLiteral(
-                                    *loc,
-                                    *array_length as u16 * 8,
-                                    BigInt::from_u8(3).unwrap(),
-                                )),
-                            )),
-                            false,
-                        )),
-                    ),
-                    res_ty,
-                ))
-            }
-            resolver::Type::Array(_, dim) if dim.last().unwrap().is_some() => Ok((
-                Expression::ArraySubscript(
-                    *loc,
-                    Box::new(cast(
-                        &array.loc(),
-                        array_expr,
-                        &array_ty,
-                        &array_ty.deref(),
-                        true,
-                        ns,
-                        errors,
-                    )?),
-                    Box::new(Expression::Variable(index.loc(), pos)),
-                ),
-                array_ty.array_deref(),
-            )),
-            resolver::Type::DynamicBytes | resolver::Type::Array(_, _) => Ok((
-                Expression::DynamicArraySubscript(
-                    *loc,
-                    Box::new(cast(
-                        &array.loc(),
-                        array_expr,
-                        &array_ty,
-                        &array_ty.deref(),
-                        true,
-                        ns,
-                        errors,
-                    )?),
-                    array_ty.array_deref(),
-                    Box::new(Expression::Variable(index.loc(), pos)),
-                ),
-                array_ty.array_deref(),
-            )),
-            _ => {
-                // should not happen as type-checking already done
-                unreachable!();
-            }
-        }
-    }
-}
-
-/// Resolve a function call with positional arguments
-fn struct_literal(
-    loc: &pt::Loc,
-    struct_no: usize,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let struct_def = &ns.structs[struct_no];
-
-    if args.len() != struct_def.fields.len() {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "struct ‘{}’ has {} fields, not {}",
-                struct_def.name,
-                struct_def.fields.len(),
-                args.len()
-            ),
-        ));
-        Err(())
-    } else {
-        let mut fields = Vec::new();
-
-        for (i, a) in args.iter().enumerate() {
-            let (expr, expr_type) = expression(&a, cfg, contract_no, ns, vartab, errors)?;
-
-            fields.push(cast(
-                loc,
-                expr,
-                &expr_type,
-                &struct_def.fields[i].ty,
-                true,
-                ns,
-                errors,
-            )?);
-        }
-
-        let ty = resolver::Type::Struct(struct_no);
-
-        Ok((Expression::StructLiteral(*loc, ty.clone(), fields), ty))
-    }
-}
-
-/// Resolve a function call with positional arguments
-fn function_call_pos_args(
-    loc: &pt::Loc,
-    id: &pt::Identifier,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    // Try to resolve as a function call
-    let funcs = ns.resolve_func(contract_no.unwrap(), &id, errors)?;
-
-    let mut resolved_args = Vec::new();
-    let mut resolved_types = Vec::new();
-
-    for arg in args {
-        let (expr, expr_type) = expression(arg, cfg, contract_no, ns, vartab, errors)?;
-
-        resolved_args.push(Box::new(expr));
-        resolved_types.push(expr_type);
-    }
-
-    let mut temp_errors = Vec::new();
-
-    // function call
-    for f in funcs {
-        let func = &ns.contracts[contract_no.unwrap()].functions[f.1];
-
-        if func.params.len() != args.len() {
-            temp_errors.push(Output::error(
-                *loc,
-                format!(
-                    "function expects {} arguments, {} provided",
-                    func.params.len(),
-                    args.len()
-                ),
-            ));
-            continue;
-        }
-
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for (i, param) in func.params.iter().enumerate() {
-            let arg = &resolved_args[i];
-
-            match cast(
-                &pt::Loc(0, 0),
-                *arg.clone(),
-                &resolved_types[i],
-                &param.ty,
-                true,
-                ns,
-                &mut temp_errors,
-            ) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                    break;
-                }
-            }
-        }
-
-        if matches {
-            return Ok((
-                Expression::LocalFunctionCall(*loc, f.1, cast_args),
-                resolver::Type::Undef,
-            ));
-        }
-    }
-
-    if funcs.len() == 1 {
-        errors.append(&mut temp_errors);
-    } else {
-        errors.push(Output::error(
-            *loc,
-            "cannot find overloaded function which matches signature".to_string(),
-        ));
-    }
-
-    Err(())
-}
-
-/// Resolve a function call with named arguments
-fn function_call_with_named_args(
-    loc: &pt::Loc,
-    id: &pt::Identifier,
-    args: &[pt::NamedArgument],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    // Try to resolve as a function call
-    let funcs = ns.resolve_func(contract_no.unwrap(), &id, errors)?;
-
-    let mut arguments = HashMap::new();
-
-    for arg in args {
-        if arguments.contains_key(&arg.name.name) {
-            errors.push(Output::error(
-                arg.name.loc,
-                format!("duplicate argument with name ‘{}’", arg.name.name),
-            ));
-            return Err(());
-        }
-        arguments.insert(
-            arg.name.name.to_string(),
-            expression(&arg.expr, cfg, contract_no, ns, vartab, errors)?,
-        );
-    }
-
-    let mut temp_errors = Vec::new();
-
-    // function call
-    for f in funcs {
-        let func = &ns.contracts[contract_no.unwrap()].functions[f.1];
-
-        if func.params.len() != args.len() {
-            temp_errors.push(Output::error(
-                *loc,
-                format!(
-                    "function expects {} arguments, {} provided",
-                    func.params.len(),
-                    args.len()
-                ),
-            ));
-            continue;
-        }
-
-        let mut matches = true;
-        let mut cast_args = Vec::new();
-
-        // check if arguments can be implicitly casted
-        for param in func.params.iter() {
-            let arg = match arguments.get(&param.name) {
-                Some(a) => a,
-                None => {
-                    matches = false;
-                    temp_errors.push(Output::error(
-                        *loc,
-                        format!(
-                            "missing argument ‘{}’ to function ‘{}’",
-                            param.name, func.name,
-                        ),
-                    ));
-                    break;
-                }
-            };
-
-            match cast(
-                &pt::Loc(0, 0),
-                arg.0.clone(),
-                &arg.1,
-                &param.ty,
-                true,
-                ns,
-                &mut temp_errors,
-            ) {
-                Ok(expr) => cast_args.push(expr),
-                Err(()) => {
-                    matches = false;
-                    break;
-                }
-            }
-        }
-
-        if matches {
-            return Ok((
-                Expression::LocalFunctionCall(*loc, f.1, cast_args),
-                resolver::Type::Undef,
-            ));
-        }
-    }
-
-    if funcs.len() == 1 {
-        errors.append(&mut temp_errors);
-    } else {
-        errors.push(Output::error(
-            *loc,
-            "cannot find overloaded function which matches signature".to_string(),
-        ));
-    }
-
-    Err(())
-}
-
-/// Resolve a struct literal with named fields
-fn named_struct_literal(
-    loc: &pt::Loc,
-    struct_no: usize,
-    args: &[pt::NamedArgument],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let struct_def = &ns.structs[struct_no];
-
-    if args.len() != struct_def.fields.len() {
-        errors.push(Output::error(
-            *loc,
-            format!(
-                "struct ‘{}’ has {} fields, not {}",
-                struct_def.name,
-                struct_def.fields.len(),
-                args.len()
-            ),
-        ));
-        Err(())
-    } else {
-        let mut fields = Vec::new();
-        fields.resize(args.len(), Expression::Poison);
-        for a in args {
-            match struct_def
-                .fields
-                .iter()
-                .enumerate()
-                .find(|(_, f)| f.name == a.name.name)
-            {
-                Some((i, f)) => {
-                    let (expr, expr_type) =
-                        expression(&a.expr, cfg, contract_no, ns, vartab, errors)?;
-
-                    fields[i] = cast(loc, expr, &expr_type, &f.ty, true, ns, errors)?;
-                }
-                None => {
-                    errors.push(Output::error(
-                        a.name.loc,
-                        format!(
-                            "struct ‘{}’ has no field ‘{}’",
-                            struct_def.name, a.name.name,
-                        ),
-                    ));
-                    return Err(());
-                }
-            }
-        }
-        let ty = resolver::Type::Struct(struct_no);
-        Ok((Expression::StructLiteral(*loc, ty.clone(), fields), ty))
-    }
-}
-
-/// Resolve a method call with positional arguments
-fn method_call_pos_args(
-    loc: &pt::Loc,
-    var: &pt::Expression,
-    func: &pt::Identifier,
-    args: &[pt::Expression],
-    call_args: &[&pt::NamedArgument],
-    call_args_loc: Option<pt::Loc>,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (var_expr, var_ty) = expression(var, cfg, contract_no, ns, vartab, errors)?;
-
-    if let resolver::Type::StorageRef(ty) = &var_ty {
-        match ty.as_ref() {
-            resolver::Type::Array(_, dim) => {
-                if let Some(loc) = call_args_loc {
-                    errors.push(Output::error(
-                        loc,
-                        "call arguments not allowed on arrays".to_string(),
-                    ));
-                    return Err(());
-                }
-
-                if func.name == "push" {
-                    return if dim.last().unwrap().is_some() {
-                        errors.push(Output::error(
-                            func.loc,
-                            "method ‘push()’ not allowed on fixed length array".to_string(),
-                        ));
-                        Err(())
-                    } else {
-                        array_push(
-                            loc,
-                            var_expr,
-                            func,
-                            ty,
-                            args,
-                            cfg,
-                            contract_no,
-                            ns,
-                            vartab,
-                            errors,
-                        )
-                    };
-                }
-                if func.name == "pop" {
-                    return if dim.last().unwrap().is_some() {
-                        errors.push(Output::error(
-                            func.loc,
-                            "method ‘pop()’ not allowed on fixed length array".to_string(),
-                        ));
-                        Err(())
-                    } else {
-                        array_pop(loc, var_expr, func, ty, args, cfg, ns, vartab, errors)
-                    };
-                }
-            }
-            resolver::Type::DynamicBytes => {
-                if let Some(loc) = call_args_loc {
-                    errors.push(Output::error(
-                        loc,
-                        "call arguments not allowed on bytes".to_string(),
-                    ));
-                    return Err(());
-                }
-
-                match func.name.as_str() {
-                    "push" => {
-                        return bytes_push(
-                            loc,
-                            var_expr,
-                            func,
-                            args,
-                            cfg,
-                            contract_no,
-                            ns,
-                            vartab,
-                            errors,
-                        );
-                    }
-                    "pop" => {
-                        return bytes_pop(loc, var_expr, func, args, cfg, errors);
-                    }
-                    _ => {}
-                }
-            }
-            _ => {}
-        }
-    }
-
-    if let resolver::Type::Contract(contract_no) = &var_ty.deref() {
-        let call_args =
-            parse_call_args(call_args, true, cfg, Some(*contract_no), ns, vartab, errors)?;
-
-        let mut resolved_args = Vec::new();
-        let mut resolved_types = Vec::new();
-
-        for arg in args {
-            let (expr, expr_type) = expression(arg, cfg, Some(*contract_no), ns, vartab, errors)?;
-            resolved_args.push(Box::new(expr));
-            resolved_types.push(expr_type);
-        }
-
-        let mut temp_errors = Vec::new();
-        let mut name_match = 0;
-
-        for (n, ftype) in ns.contracts[*contract_no].functions.iter().enumerate() {
-            if func.name != ftype.name {
-                continue;
-            }
-
-            name_match += 1;
-
-            if ftype.params.len() != args.len() {
-                temp_errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "function expects {} arguments, {} provided",
-                        ftype.params.len(),
-                        args.len()
-                    ),
-                ));
-                continue;
-            }
-            let mut matches = true;
-            let mut cast_args = Vec::new();
-            // check if arguments can be implicitly casted
-            for (i, param) in ftype.params.iter().enumerate() {
-                let arg = &resolved_args[i];
-                match cast(
-                    &pt::Loc(0, 0),
-                    *arg.clone(),
-                    &resolved_types[i],
-                    &param.ty,
-                    true,
-                    ns,
-                    &mut temp_errors,
-                ) {
-                    Ok(expr) => cast_args.push(expr),
-                    Err(()) => {
-                        matches = false;
-                        break;
-                    }
-                }
-            }
-            if matches {
-                if !ftype.is_public() {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("function ‘{}’ is not ‘public’ or ‘extern’", ftype.name),
-                    ));
-                    return Err(());
-                }
-
-                let value = if let Some(value) = call_args.value {
-                    if !value.const_zero() && !ftype.is_payable() {
-                        errors.push(Output::error(
-                            *loc,
-                            format!(
-                                "sending value to function ‘{}’ which is not payable",
-                                func.name
-                            ),
-                        ));
-                        return Err(());
-                    }
-
-                    value
-                } else {
-                    Box::new(Expression::NumberLiteral(
-                        pt::Loc(0, 0),
-                        ns.value_length as u16 * 8,
-                        BigInt::zero(),
-                    ))
-                };
-
-                return Ok((
-                    Expression::ExternalFunctionCall {
-                        loc: *loc,
-                        contract_no: *contract_no,
-                        function_no: n,
-                        address: Box::new(cast(
-                            &var.loc(),
-                            var_expr,
-                            &var_ty,
-                            &resolver::Type::Contract(*contract_no),
-                            true,
-                            ns,
-                            errors,
-                        )?),
-                        args: cast_args,
-                        value,
-                        gas: call_args.gas,
-                    },
-                    resolver::Type::Undef,
-                ));
-            }
-        }
-
-        if name_match == 1 {
-            errors.append(&mut temp_errors);
-        } else {
-            errors.push(Output::error(
-                *loc,
-                "cannot find overloaded function which matches signature".to_string(),
-            ));
-        }
-
-        return Err(());
-    }
-
-    if let resolver::Type::Address(true) = &var_ty.deref() {
-        if func.name == "transfer" || func.name == "send" {
-            if args.len() != 1 {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "‘{}’ expects 1 argument, {} provided",
-                        func.name,
-                        args.len()
-                    ),
-                ));
-
-                return Err(());
-            }
-
-            if let Some(loc) = call_args_loc {
-                errors.push(Output::error(
-                    loc,
-                    format!("call arguments not allowed on ‘{}’", func.name),
-                ));
-                return Err(());
-            }
-
-            let (expr, expr_type) = expression(&args[0], cfg, contract_no, ns, vartab, errors)?;
-
-            let value = cast(
-                &args[0].loc(),
-                expr,
-                &expr_type,
-                &resolver::Type::Uint(ns.value_length as u16 * 8),
-                true,
-                ns,
-                errors,
-            )?;
-
-            let tab = match vartab {
-                &mut Some(ref mut tab) => tab,
-                None => unreachable!(),
-            };
-
-            let success = if func.name == "transfer" {
-                None
-            } else {
-                Some(tab.temp(
-                    &pt::Identifier {
-                        loc: pt::Loc(0, 0),
-                        name: "success".to_owned(),
-                    },
-                    &resolver::Type::Bool,
-                ))
-            };
-
-            cfg.add(
-                tab,
-                Instr::ExternalCall {
-                    success,
-                    address: cast(
-                        &var.loc(),
-                        var_expr,
-                        &var_ty,
-                        &resolver::Type::Address(true),
-                        true,
-                        ns,
-                        errors,
-                    )?,
-                    contract_no: None,
-                    function_no: 0,
-                    args: Vec::new(),
-                    value,
-                    gas: Expression::NumberLiteral(*loc, 64, BigInt::zero()),
-                },
-            );
-
-            return match success {
-                Some(pos) => Ok((Expression::Variable(*loc, pos), resolver::Type::Bool)),
-                None => Ok((Expression::Poison, resolver::Type::Undef)),
-            };
-        }
-    }
-
-    errors.push(Output::error(
-        func.loc,
-        format!("method ‘{}’ does not exist", func.name),
-    ));
-
-    Err(())
-}
-
-fn method_call_with_named_args(
-    loc: &pt::Loc,
-    var: &pt::Expression,
-    func_name: &pt::Identifier,
-    args: &[pt::NamedArgument],
-    call_args: &[&pt::NamedArgument],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (var_expr, var_ty) = expression(var, cfg, contract_no, ns, vartab, errors)?;
-
-    if let resolver::Type::Contract(external_contract_no) = &var_ty.deref() {
-        let call_args = parse_call_args(&call_args, true, cfg, contract_no, ns, vartab, errors)?;
-
-        let mut arguments = HashMap::new();
-
-        for arg in args {
-            if arguments.contains_key(&arg.name.name) {
-                errors.push(Output::error(
-                    arg.name.loc,
-                    format!("duplicate argument with name ‘{}’", arg.name.name),
-                ));
-                return Err(());
-            }
-            arguments.insert(
-                arg.name.name.to_string(),
-                expression(&arg.expr, cfg, contract_no, ns, vartab, errors)?,
-            );
-        }
-
-        let mut temp_errors = Vec::new();
-
-        let mut name_match = 0;
-
-        // function call
-        for (func_no, func) in ns.contracts[*external_contract_no]
-            .functions
-            .iter()
-            .enumerate()
-        {
-            if func.name != func_name.name {
-                continue;
-            }
-
-            name_match += 1;
-
-            if func.params.len() != args.len() {
-                temp_errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "function expects {} arguments, {} provided",
-                        func.params.len(),
-                        args.len()
-                    ),
-                ));
-                continue;
-            }
-            let mut matches = true;
-            let mut cast_args = Vec::new();
-            // check if arguments can be implicitly casted
-            for param in func.params.iter() {
-                let arg = match arguments.get(&param.name) {
-                    Some(a) => a,
-                    None => {
-                        matches = false;
-                        temp_errors.push(Output::error(
-                            *loc,
-                            format!(
-                                "missing argument ‘{}’ to function ‘{}’",
-                                param.name, func.name,
-                            ),
-                        ));
-                        break;
-                    }
-                };
-                match cast(
-                    &pt::Loc(0, 0),
-                    arg.0.clone(),
-                    &arg.1,
-                    &param.ty,
-                    true,
-                    ns,
-                    &mut temp_errors,
-                ) {
-                    Ok(expr) => cast_args.push(expr),
-                    Err(()) => {
-                        matches = false;
-                        break;
-                    }
-                }
-            }
-
-            if matches {
-                if !func.is_public() {
-                    errors.push(Output::error(
-                        *loc,
-                        format!("function ‘{}’ is not ‘public’ or ‘extern’", func.name),
-                    ));
-                    return Err(());
-                }
-
-                let value = if let Some(value) = call_args.value {
-                    if !value.const_zero() && !func.is_payable() {
-                        errors.push(Output::error(
-                            *loc,
-                            format!(
-                                "sending value to function ‘{}’ which is not payable",
-                                func.name
-                            ),
-                        ));
-                        return Err(());
-                    }
-
-                    value
-                } else {
-                    Box::new(Expression::NumberLiteral(
-                        pt::Loc(0, 0),
-                        ns.value_length as u16 * 8,
-                        BigInt::zero(),
-                    ))
-                };
-
-                return Ok((
-                    Expression::ExternalFunctionCall {
-                        loc: *loc,
-                        contract_no: *external_contract_no,
-                        function_no: func_no,
-                        address: Box::new(cast(
-                            &var.loc(),
-                            var_expr,
-                            &var_ty,
-                            &resolver::Type::Contract(*external_contract_no),
-                            true,
-                            ns,
-                            errors,
-                        )?),
-                        args: cast_args,
-                        value,
-                        gas: call_args.gas,
-                    },
-                    resolver::Type::Undef,
-                ));
-            }
-        }
-
-        match name_match {
-            0 => {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "contract ‘{}’ does not have function ‘{}’",
-                        var_ty.deref().to_string(ns),
-                        func_name.name
-                    ),
-                ));
-            }
-            1 => {
-                errors.append(&mut temp_errors);
-            }
-            _ => {
-                errors.push(Output::error(
-                    *loc,
-                    "cannot find overloaded function which matches signature".to_string(),
-                ));
-            }
-        }
-        return Err(());
-    }
-
-    errors.push(Output::error(
-        func_name.loc,
-        format!("method ‘{}’ does not exist", func_name.name),
-    ));
-
-    Err(())
-}
-
-// When generating shifts, llvm wants both arguments to have the same width. We want the
-// result of the shift to be left argument, so this function coercies the right argument
-// into the right length.
-fn cast_shift_arg(
-    loc: &pt::Loc,
-    expr: Expression,
-    from_width: u16,
-    ty: &resolver::Type,
-    ns: &resolver::Namespace,
-) -> Expression {
-    let to_width = ty.bits(ns);
-
-    if from_width == to_width {
-        expr
-    } else if from_width < to_width && ty.signed() {
-        Expression::SignExt(*loc, ty.clone(), Box::new(expr))
-    } else if from_width < to_width && !ty.signed() {
-        Expression::ZeroExt(*loc, ty.clone(), Box::new(expr))
-    } else {
-        Expression::Trunc(*loc, ty.clone(), Box::new(expr))
-    }
-}
-
-/// Given an parsed literal array, ensure that it is valid. All the elements in the array
-/// must of the same type. The array might be a multidimensional array; all the leaf nodes
-/// must match.
-fn resolve_array_literal(
-    loc: &pt::Loc,
-    exprs: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let mut dims = Box::new(Vec::new());
-    let mut flattened = Vec::new();
-
-    check_subarrays(exprs, &mut Some(&mut dims), &mut flattened, errors)?;
-
-    if flattened.is_empty() {
-        errors.push(Output::error(
-            *loc,
-            "array requires at least one element".to_string(),
-        ));
-        return Err(());
-    }
-
-    let mut flattened = flattened.iter();
-
-    // We follow the solidity scheme were everthing gets implicitly converted to the
-    // type of the first element
-    let (first, ty) = expression(
-        flattened.next().unwrap(),
-        cfg,
-        contract_no,
-        ns,
-        vartab,
-        errors,
-    )?;
-
-    let mut exprs = vec![first];
-
-    for e in flattened {
-        let (mut other, oty) = expression(e, cfg, contract_no, ns, vartab, errors)?;
-
-        if oty != ty {
-            other = cast(&e.loc(), other, &oty, &ty, true, ns, errors)?;
-        }
-
-        exprs.push(other);
-    }
-
-    let aty = resolver::Type::Array(
-        Box::new(ty),
-        dims.iter()
-            .map(|n| Some(BigInt::from_u32(*n).unwrap()))
-            .collect::<Vec<Option<BigInt>>>(),
-    );
-
-    if vartab.is_none() {
-        Ok((Expression::ConstArrayLiteral(*loc, *dims, exprs), aty))
-    } else {
-        Ok((
-            Expression::ArrayLiteral(*loc, aty.clone(), *dims, exprs),
-            aty,
-        ))
-    }
-}
-
-/// Traverse the literal looking for sub arrays. Ensure that all the sub
-/// arrays are the same length, and returned a flattened array of elements
-fn check_subarrays<'a>(
-    exprs: &'a [pt::Expression],
-    dims: &mut Option<&mut Vec<u32>>,
-    flatten: &mut Vec<&'a pt::Expression>,
-    errors: &mut Vec<output::Output>,
-) -> Result<(), ()> {
-    if let Some(pt::Expression::ArrayLiteral(_, first)) = exprs.get(0) {
-        // ensure all elements are array literals of the same length
-        check_subarrays(first, dims, flatten, errors)?;
-
-        for (i, e) in exprs.iter().enumerate().skip(1) {
-            if let pt::Expression::ArrayLiteral(_, other) = e {
-                if other.len() != first.len() {
-                    errors.push(Output::error(
-                        e.loc(),
-                        format!(
-                            "array elements should be identical, sub array {} has {} elements rather than {}", i + 1, other.len(), first.len()
-                        ),
-                    ));
-                    return Err(());
-                }
-                check_subarrays(other, &mut None, flatten, errors)?;
-            } else {
-                errors.push(Output::error(
-                    e.loc(),
-                    format!("array element {} should also be an array", i + 1),
-                ));
-                return Err(());
-            }
-        }
-    } else {
-        for (i, e) in exprs.iter().enumerate().skip(1) {
-            if let pt::Expression::ArrayLiteral(loc, _) = e {
-                errors.push(Output::error(
-                    *loc,
-                    format!(
-                        "array elements should be of the type, element {} is unexpected array",
-                        i + 1
-                    ),
-                ));
-                return Err(());
-            }
-        }
-        flatten.extend(exprs);
-    }
-
-    if let Some(dims) = dims.as_deref_mut() {
-        dims.push(exprs.len() as u32);
-    }
-
-    Ok(())
-}
-
-/// The parser generates parameter lists for lists. Sometimes this needs to be a
-/// simple expression list.
-pub fn parameter_list_to_expr_list<'a>(
-    e: &'a pt::Expression,
-    errors: &mut Vec<output::Output>,
-) -> Result<Vec<&'a pt::Expression>, ()> {
-    if let pt::Expression::List(_, v) = &e {
-        let mut list = Vec::new();
-        let mut broken = false;
-
-        for e in v {
-            match &e.1 {
-                None => {
-                    errors.push(Output::error(e.0, "stray comma".to_string()));
-                    broken = true;
-                }
-                Some(pt::Parameter {
-                    name: Some(name), ..
-                }) => {
-                    errors.push(Output::error(name.loc, "single value expected".to_string()));
-                    broken = true;
-                }
-                Some(pt::Parameter {
-                    storage: Some(storage),
-                    ..
-                }) => {
-                    errors.push(Output::error(
-                        *storage.loc(),
-                        "storage specified not permitted here".to_string(),
-                    ));
-                    broken = true;
-                }
-                Some(pt::Parameter { ty, .. }) => {
-                    list.push(ty);
-                }
-            }
-        }
-
-        if !broken {
-            Ok(list)
-        } else {
-            Err(())
-        }
-    } else {
-        Ok(vec![e])
-    }
-}
-
-/// Function call arguments
-pub fn collect_call_args<'a>(
-    expr: &'a pt::Expression,
-    errors: &mut Vec<Output>,
-) -> Result<
-    (
-        &'a pt::Expression,
-        Vec<&'a pt::NamedArgument>,
-        Option<pt::Loc>,
-    ),
-    (),
-> {
-    let mut named_arguments = Vec::new();
-    let mut expr = expr;
-    let mut loc: Option<pt::Loc> = None;
-
-    while let pt::Expression::FunctionCallBlock(_, e, block) = expr {
-        match block.as_ref() {
-            pt::Statement::Args(_, args) => {
-                if let Some(l) = loc {
-                    loc = Some(pt::Loc(l.0, block.loc().1));
-                } else {
-                    loc = Some(block.loc());
-                }
-
-                named_arguments.extend(args);
-            }
-            pt::Statement::Block(_, s) if s.is_empty() => {
-                // {}
-                errors.push(Output::error(
-                    block.loc(),
-                    "missing call arguments".to_string(),
-                ));
-                return Err(());
-            }
-            _ => {
-                errors.push(Output::error(
-                    block.loc(),
-                    "code block found where list of call arguments expected, like ‘{gas: 5000}’"
-                        .to_string(),
-                ));
-                return Err(());
-            }
-        }
-
-        expr = e;
-    }
-
-    Ok((expr, named_arguments, loc))
-}
-
-struct CallArgs {
-    gas: Box<Expression>,
-    salt: Option<Box<Expression>>,
-    value: Option<Box<Expression>>,
-}
-
-/// Parse call arguments for external calls
-fn parse_call_args(
-    call_args: &[&pt::NamedArgument],
-    external_call: bool,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<CallArgs, ()> {
-    let mut args: HashMap<&String, &pt::NamedArgument> = HashMap::new();
-
-    for arg in call_args {
-        if let Some(prev) = args.get(&arg.name.name) {
-            errors.push(Output::error_with_note(
-                arg.loc,
-                format!("‘{}’ specified multiple times", arg.name.name),
-                prev.loc,
-                format!("location of previous declaration of ‘{}’", arg.name.name),
-            ));
-            return Err(());
-        }
-
-        args.insert(&arg.name.name, arg);
-    }
-
-    let mut res = CallArgs {
-        gas: Box::new(Expression::NumberLiteral(pt::Loc(0, 0), 64, BigInt::zero())),
-        value: None,
-        salt: None,
-    };
-
-    for arg in args.values() {
-        match arg.name.name.as_str() {
-            "value" => {
-                let (expr, expr_ty) = expression(&arg.expr, cfg, contract_no, ns, vartab, errors)?;
-
-                let ty = resolver::Type::Uint(ns.value_length as u16 * 8);
-
-                res.value = Some(Box::new(cast(
-                    &arg.expr.loc(),
-                    expr,
-                    &expr_ty,
-                    &ty,
-                    true,
-                    ns,
-                    errors,
-                )?));
-            }
-            "gas" => {
-                let (expr, expr_ty) = expression(&arg.expr, cfg, contract_no, ns, vartab, errors)?;
-
-                let ty = resolver::Type::Uint(64);
-
-                res.gas = Box::new(cast(
-                    &arg.expr.loc(),
-                    expr,
-                    &expr_ty,
-                    &ty,
-                    true,
-                    ns,
-                    errors,
-                )?);
-            }
-            "salt" => {
-                if external_call {
-                    errors.push(Output::error(
-                        arg.loc,
-                        "‘salt’ not valid for external calls".to_string(),
-                    ));
-                    return Err(());
-                }
-
-                let (expr, expr_ty) = expression(&arg.expr, cfg, contract_no, ns, vartab, errors)?;
-
-                let ty = resolver::Type::Uint(256);
-
-                res.salt = Some(Box::new(cast(
-                    &arg.expr.loc(),
-                    expr,
-                    &expr_ty,
-                    &ty,
-                    true,
-                    ns,
-                    errors,
-                )?));
-            }
-            _ => {
-                errors.push(Output::error(
-                    arg.loc,
-                    format!("‘{}’ not a valid call parameter", arg.name.name),
-                ));
-                return Err(());
-            }
-        }
-    }
-
-    Ok(res)
-}
-
-/// Resolve function call
-pub fn function_call_expr(
-    loc: &pt::Loc,
-    ty: &pt::Expression,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (ty, call_args, call_args_loc) = collect_call_args(ty, errors)?;
-
-    match ty {
-        pt::Expression::MemberAccess(_, member, func) => method_call_pos_args(
-            loc,
-            member,
-            func,
-            args,
-            &call_args,
-            call_args_loc,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            errors,
-        ),
-        pt::Expression::Variable(id) => {
-            if let Some(loc) = call_args_loc {
-                errors.push(Output::error(
-                    loc,
-                    "call arguments not permitted for internal calls".to_string(),
-                ));
-                return Err(());
-            }
-
-            function_call_pos_args(loc, &id, args, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Expression::ArraySubscript(_, _, _) => {
-            errors.push(Output::error(ty.loc(), "unexpected array type".to_string()));
-            Err(())
-        }
-        _ => {
-            errors.push(Output::error(
-                ty.loc(),
-                "expression not expected here".to_string(),
-            ));
-            Err(())
-        }
-    }
-}
-
-/// Resolve function call expression with named arguments
-pub fn named_function_call_expr(
-    loc: &pt::Loc,
-    ty: &pt::Expression,
-    args: &[pt::NamedArgument],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (ty, call_args, call_args_loc) = collect_call_args(ty, errors)?;
-
-    match ty {
-        pt::Expression::MemberAccess(_, member, func) => method_call_with_named_args(
-            loc,
-            member,
-            func,
-            args,
-            &call_args,
-            cfg,
-            contract_no,
-            ns,
-            vartab,
-            errors,
-        ),
-        pt::Expression::Variable(id) => {
-            if let Some(loc) = call_args_loc {
-                errors.push(Output::error(
-                    loc,
-                    "call arguments not permitted for internal calls".to_string(),
-                ));
-                return Err(());
-            }
-
-            function_call_with_named_args(loc, &id, args, cfg, contract_no, ns, vartab, errors)
-        }
-        pt::Expression::ArraySubscript(_, _, _) => {
-            errors.push(Output::error(ty.loc(), "unexpected array type".to_string()));
-            Err(())
-        }
-        _ => {
-            errors.push(Output::error(
-                ty.loc(),
-                "expression not expected here".to_string(),
-            ));
-            Err(())
-        }
-    }
-}
-
-/// Convert a function call expression to CFG in expression context
-fn emit_function_call(
-    expr: Expression,
-    expr_ty: resolver::Type,
-    contract_no: usize,
-    cfg: &mut ControlFlowGraph,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-) -> Vec<(Expression, resolver::Type)> {
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => unreachable!(),
-    };
-
-    match expr {
-        Expression::LocalFunctionCall(_, func, args) => {
-            let ftype = &ns.contracts[contract_no].functions[func];
-
-            if !ftype.returns.is_empty() {
-                let mut res = Vec::new();
-                let mut returns = Vec::new();
-
-                for ret in &ftype.returns {
-                    let id = pt::Identifier {
-                        loc: pt::Loc(0, 0),
-                        name: ret.name.to_owned(),
-                    };
-
-                    let temp_pos = tab.temp(&id, &ret.ty);
-                    res.push(temp_pos);
-                    returns.push((Expression::Variable(id.loc, temp_pos), ret.ty.clone()));
-                }
-
-                cfg.add(tab, Instr::Call { res, func, args });
-
-                returns
-            } else {
-                cfg.add(
-                    tab,
-                    Instr::Call {
-                        res: Vec::new(),
-                        func,
-                        args,
-                    },
-                );
-
-                vec![(
-                    if ftype.noreturn {
-                        Expression::Unreachable
-                    } else {
-                        Expression::Poison
-                    },
-                    resolver::Type::Undef,
-                )]
-            }
-        }
-        Expression::ExternalFunctionCall {
-            loc,
-            contract_no,
-            function_no,
-            address,
-            args,
-            value,
-            gas,
-        } => {
-            let ftype = &ns.contracts[contract_no].functions[function_no];
-
-            cfg.add(
-                tab,
-                Instr::ExternalCall {
-                    success: None,
-                    address: *address,
-                    contract_no: Some(contract_no),
-                    function_no,
-                    args,
-                    value: *value,
-                    gas: *gas,
-                },
-            );
-
-            if !ftype.returns.is_empty() {
-                let mut returns = Vec::new();
-                let mut res = Vec::new();
-
-                for ret in &ftype.returns {
-                    let id = pt::Identifier {
-                        loc: pt::Loc(0, 0),
-                        name: "".to_owned(),
-                    };
-                    let temp_pos = tab.temp(&id, &ret.ty);
-                    res.push(temp_pos);
-                    returns.push((Expression::Variable(id.loc, temp_pos), ret.ty.clone()));
-                }
-
-                cfg.add(
-                    tab,
-                    Instr::AbiDecode {
-                        res,
-                        selector: None,
-                        exception: None,
-                        tys: ftype.returns.clone(),
-                        data: Expression::ReturnData(loc),
-                    },
-                );
-
-                returns
-            } else {
-                vec![(Expression::Poison, resolver::Type::Undef)]
-            }
-        }
-        _ => vec![(expr, expr_ty)],
-    }
-}
-
-/// Convert a constructor call expression to CFG in expression context
-fn emit_constructor_call(
-    expr: Expression,
-    expr_ty: resolver::Type,
-    cfg: &mut ControlFlowGraph,
-    vartab: &mut Option<&mut Vartable>,
-) -> (Expression, resolver::Type) {
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => unreachable!(),
-    };
-
-    match expr {
-        Expression::Constructor {
-            loc,
-            contract_no,
-            constructor_no,
-            args,
-            value,
-            gas,
-            salt,
-        } => {
-            let address_res = tab.temp_anonymous(&resolver::Type::Contract(contract_no));
-
-            cfg.add(
-                tab,
-                Instr::Constructor {
-                    success: None,
-                    res: address_res,
-                    contract_no,
-                    constructor_no,
-                    args,
-                    value: value.map(|v| *v),
-                    gas: *gas,
-                    salt: salt.map(|v| *v),
-                },
-            );
-
-            (
-                Expression::Variable(loc, address_res),
-                resolver::Type::Contract(contract_no),
-            )
-        }
-        _ => (expr, expr_ty),
-    }
-}

+ 0 - 1442
src/resolver/mod.rs

@@ -1,1442 +0,0 @@
-use abi;
-use emit;
-use num_bigint::BigInt;
-use num_traits::Signed;
-use num_traits::{One, Zero};
-use output::{any_errors, Note, Output};
-use parser::pt;
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::ops::Mul;
-use tiny_keccak::keccak256;
-use Target;
-
-mod address;
-mod builtin;
-pub mod cfg;
-mod eval;
-pub mod expression;
-mod functions;
-mod storage;
-mod types;
-mod variables;
-
-use inkwell::OptimizationLevel;
-use resolver::cfg::{ControlFlowGraph, Instr, Vartable};
-use resolver::eval::eval_number_expression;
-use resolver::expression::{expression, Expression};
-
-pub type ArrayDimension = Option<(pt::Loc, BigInt)>;
-
-#[derive(PartialEq, Clone, Debug)]
-pub enum Type {
-    Address(bool),
-    Bool,
-    Int(u16),
-    Uint(u16),
-    Bytes(u8),
-    DynamicBytes,
-    String,
-    Array(Box<Type>, Vec<Option<BigInt>>),
-    Enum(usize),
-    Struct(usize),
-    Mapping(Box<Type>, Box<Type>),
-    Contract(usize),
-    Ref(Box<Type>),
-    StorageRef(Box<Type>),
-    Undef,
-}
-
-impl Type {
-    pub fn to_string(&self, ns: &Namespace) -> String {
-        match self {
-            Type::Bool => "bool".to_string(),
-            Type::Address(false) => "address".to_string(),
-            Type::Address(true) => "address payable".to_string(),
-            Type::Int(n) => format!("int{}", n),
-            Type::Uint(n) => format!("uint{}", n),
-            Type::Bytes(n) => format!("bytes{}", n),
-            Type::String => "string".to_string(),
-            Type::DynamicBytes => "bytes".to_string(),
-            Type::Enum(n) => format!("enum {}", ns.enums[*n].print_to_string()),
-            Type::Struct(n) => format!("struct {}", ns.structs[*n].print_to_string()),
-            Type::Array(ty, len) => format!(
-                "{}{}",
-                ty.to_string(ns),
-                len.iter()
-                    .map(|l| match l {
-                        None => "[]".to_string(),
-                        Some(l) => format!("[{}]", l),
-                    })
-                    .collect::<String>()
-            ),
-            Type::Mapping(k, v) => format!("mapping({} => {})", k.to_string(ns), v.to_string(ns)),
-            Type::Contract(n) => format!("contract {}", ns.contracts[*n].name),
-            Type::Ref(r) => r.to_string(ns),
-            Type::StorageRef(ty) => format!("{} storage", ty.to_string(ns)),
-            Type::Undef => "undefined".to_owned(),
-        }
-    }
-
-    /// Is this a primitive, i.e. bool, address, int, uint, bytes
-    pub fn is_primitive(&self) -> bool {
-        match self {
-            Type::Bool => true,
-            Type::Address(_) => true,
-            Type::Int(_) => true,
-            Type::Uint(_) => true,
-            Type::Bytes(_) => true,
-            Type::Ref(r) => r.is_primitive(),
-            Type::StorageRef(r) => r.is_primitive(),
-            _ => false,
-        }
-    }
-
-    pub fn to_signature_string(&self, ns: &Namespace) -> String {
-        match self {
-            Type::Bool => "bool".to_string(),
-            Type::Contract(_) | Type::Address(_) => "address".to_string(),
-            Type::Int(n) => format!("int{}", n),
-            Type::Uint(n) => format!("uint{}", n),
-            Type::Bytes(n) => format!("bytes{}", n),
-            Type::DynamicBytes => "bytes".to_string(),
-            Type::String => "string".to_string(),
-            Type::Enum(n) => ns.enums[*n].ty.to_signature_string(ns),
-            Type::Array(ty, len) => format!(
-                "{}{}",
-                ty.to_signature_string(ns),
-                len.iter()
-                    .map(|l| match l {
-                        None => "[]".to_string(),
-                        Some(l) => format!("[{}]", l),
-                    })
-                    .collect::<String>()
-            ),
-            Type::Ref(r) => r.to_string(ns),
-            Type::StorageRef(r) => r.to_string(ns),
-            Type::Struct(_) => "tuple".to_owned(),
-            Type::Mapping(_, _) => unreachable!(),
-            Type::Undef => "undefined".to_owned(),
-        }
-    }
-
-    /// Give the type of an memory array after dereference.
-    pub fn array_deref(&self) -> Self {
-        match self {
-            Type::String | Type::DynamicBytes => Type::Ref(Box::new(Type::Uint(8))),
-            Type::Ref(t) => t.array_deref(),
-            Type::Array(ty, dim) if dim.len() > 1 => {
-                Type::Array(ty.clone(), dim[..dim.len() - 1].to_vec())
-            }
-            Type::Array(ty, dim) if dim.len() == 1 => Type::Ref(Box::new(*ty.clone())),
-            _ => panic!("deref on non-array"),
-        }
-    }
-
-    /// Given an array, return the type of its elements
-    pub fn array_elem(&self) -> Self {
-        match self {
-            Type::Array(ty, dim) if dim.len() > 1 => {
-                Type::Array(ty.clone(), dim[..dim.len() - 1].to_vec())
-            }
-            Type::Array(ty, dim) if dim.len() == 1 => *ty.clone(),
-            _ => panic!("not an array"),
-        }
-    }
-
-    /// Give the type of an storage array after dereference. This can only be used on
-    /// array types and will cause a panic otherwise.
-    pub fn storage_deref(&self) -> Self {
-        match self {
-            Type::Array(ty, dim) if dim.len() > 1 => Type::StorageRef(Box::new(Type::Array(
-                ty.clone(),
-                dim[..dim.len() - 1].to_vec(),
-            ))),
-            Type::Array(ty, dim) if dim.len() == 1 => Type::StorageRef(Box::new(*ty.clone())),
-            _ => panic!("deref on non-array"),
-        }
-    }
-
-    /// Give the length of the outer array. This can only be called on array types
-    /// and will panic otherwise.
-    pub fn array_length(&self) -> Option<&BigInt> {
-        match self {
-            Type::StorageRef(ty) => ty.array_length(),
-            Type::Ref(ty) => ty.array_length(),
-            Type::Array(_, dim) => dim.last().unwrap().as_ref(),
-            _ => panic!("array_length on non-array"),
-        }
-    }
-
-    /// Calculate how much memory we expect this type to use when allocated on the
-    /// stack or on the heap. Depending on the llvm implementation there might be
-    /// padding between elements which is not accounted for.
-    pub fn size_hint(&self, ns: &Namespace) -> BigInt {
-        match self {
-            Type::Enum(_) => BigInt::one(),
-            Type::Bool => BigInt::one(),
-            Type::Contract(_) | Type::Address(_) => BigInt::from(ns.address_length),
-            Type::Bytes(n) => BigInt::from(*n),
-            Type::Uint(n) | Type::Int(n) => BigInt::from(n / 8),
-            Type::Array(ty, dims) => {
-                let pointer_size = BigInt::from(4);
-                ty.size_hint(ns).mul(
-                    dims.iter()
-                        .map(|d| match d {
-                            None => &pointer_size,
-                            Some(d) => d,
-                        })
-                        .product::<BigInt>(),
-                )
-            }
-            Type::Struct(n) => ns.structs[*n]
-                .fields
-                .iter()
-                .map(|f| f.ty.size_hint(ns))
-                .sum(),
-            Type::String | Type::DynamicBytes => BigInt::from(4),
-            _ => unimplemented!(),
-        }
-    }
-
-    pub fn bits(&self, ns: &Namespace) -> u16 {
-        match self {
-            Type::Address(_) => ns.address_length as u16 * 8,
-            Type::Bool => 1,
-            Type::Int(n) => *n,
-            Type::Uint(n) => *n,
-            Type::Bytes(n) => *n as u16 * 8,
-            _ => panic!("type not allowed"),
-        }
-    }
-
-    pub fn signed(&self) -> bool {
-        match self {
-            Type::Int(_) => true,
-            Type::Ref(r) => r.signed(),
-            Type::StorageRef(r) => r.signed(),
-            _ => false,
-        }
-    }
-
-    pub fn ordered(&self) -> bool {
-        match self {
-            Type::Int(_) => true,
-            Type::Uint(_) => true,
-            Type::Struct(_) => unreachable!(),
-            Type::Array(_, _) => unreachable!(),
-            Type::Undef => unreachable!(),
-            Type::Ref(r) => r.ordered(),
-            Type::StorageRef(r) => r.ordered(),
-            _ => false,
-        }
-    }
-
-    /// Calculate how many storage slots a type occupies. Note that storage arrays can
-    /// be very large
-    pub fn storage_slots(&self, ns: &Namespace) -> BigInt {
-        match self {
-            Type::StorageRef(r) | Type::Ref(r) => r.storage_slots(ns),
-            Type::Struct(n) => ns.structs[*n]
-                .fields
-                .iter()
-                .map(|f| f.ty.storage_slots(ns))
-                .sum(),
-            Type::Undef => unreachable!(),
-            Type::Array(ty, dims) => {
-                let one = BigInt::one();
-
-                ty.storage_slots(ns)
-                    * dims
-                        .iter()
-                        .map(|l| match l {
-                            None => &one,
-                            Some(l) => l,
-                        })
-                        .product::<BigInt>()
-            }
-            _ => BigInt::one(),
-        }
-    }
-
-    /// Is this type an reference type in the solidity language? (struct, array, mapping)
-    pub fn is_reference_type(&self) -> bool {
-        match self {
-            Type::Bool => false,
-            Type::Address(_) => false,
-            Type::Int(_) => false,
-            Type::Uint(_) => false,
-            Type::Bytes(_) => false,
-            Type::Enum(_) => false,
-            Type::Struct(_) => true,
-            Type::Array(_, _) => true,
-            Type::DynamicBytes => true,
-            Type::String => true,
-            Type::Mapping(_, _) => true,
-            Type::Contract(_) => false,
-            Type::Ref(r) => r.is_reference_type(),
-            Type::StorageRef(r) => r.is_reference_type(),
-            Type::Undef => unreachable!(),
-        }
-    }
-
-    /// Does this type contain any types which are variable-length
-    pub fn is_dynamic(&self, ns: &Namespace) -> bool {
-        match self {
-            Type::String | Type::DynamicBytes => true,
-            Type::Ref(r) => r.is_dynamic(ns),
-            Type::Array(ty, dim) => {
-                if dim.iter().any(|d| d.is_none()) {
-                    return true;
-                }
-
-                ty.is_dynamic(ns)
-            }
-            Type::Struct(n) => ns.structs[*n].fields.iter().any(|f| f.ty.is_dynamic(ns)),
-            Type::StorageRef(r) => r.is_dynamic(ns),
-            _ => false,
-        }
-    }
-
-    /// Can this type have a calldata, memory, or storage location. This is to be
-    /// compatible with ethereum solidity. Opinions on whether other types should be
-    /// allowed be storage are welcome.
-    pub fn can_have_data_location(&self) -> bool {
-        match self {
-            Type::Array(_, _)
-            | Type::Struct(_)
-            | Type::Mapping(_, _)
-            | Type::String
-            | Type::DynamicBytes => true,
-            _ => false,
-        }
-    }
-
-    /// Is this a reference to contract storage?
-    pub fn is_contract_storage(&self) -> bool {
-        match self {
-            Type::StorageRef(_) => true,
-            _ => false,
-        }
-    }
-
-    /// Is this a storage bytes string
-    pub fn is_storage_bytes(&self) -> bool {
-        if let Type::StorageRef(ty) = self {
-            if let Type::DynamicBytes = ty.as_ref() {
-                return true;
-            }
-        }
-
-        false
-    }
-
-    /// Is this a mapping
-    pub fn is_mapping(&self) -> bool {
-        match self {
-            Type::Mapping(_, _) => true,
-            Type::StorageRef(ty) => ty.is_mapping(),
-            _ => false,
-        }
-    }
-
-    /// Does the type contain any mapping type
-    pub fn contains_mapping(&self, ns: &Namespace) -> bool {
-        match self {
-            Type::Mapping(_, _) => true,
-            Type::Array(ty, _) => ty.contains_mapping(ns),
-            Type::Struct(n) => ns.structs[*n]
-                .fields
-                .iter()
-                .any(|f| f.ty.contains_mapping(ns)),
-            Type::StorageRef(r) | Type::Ref(r) => r.contains_mapping(ns),
-            _ => false,
-        }
-    }
-
-    /// If the type is Ref or StorageRef, get the underlying type
-    pub fn deref(&self) -> &Self {
-        match self {
-            Type::StorageRef(r) => r,
-            Type::Ref(r) => r,
-            _ => self,
-        }
-    }
-
-    /// If the type is Ref, get the underlying type
-    pub fn deref_nonstorage(&self) -> &Self {
-        match self {
-            Type::Ref(r) => r,
-            _ => self,
-        }
-    }
-}
-
-pub struct StructField {
-    pub name: String,
-    pub loc: pt::Loc,
-    pub ty: Type,
-}
-
-pub struct StructDecl {
-    pub name: String,
-    pub loc: pt::Loc,
-    pub contract: Option<String>,
-    pub fields: Vec<StructField>,
-}
-
-impl StructDecl {
-    /// Make the struct name into a string for printing. The enum can be declared either
-    /// inside or outside a contract.
-    pub fn print_to_string(&self) -> String {
-        match &self.contract {
-            Some(c) => format!("{}.{}", c, self.name),
-            None => self.name.to_owned(),
-        }
-    }
-}
-
-pub struct EnumDecl {
-    pub name: String,
-    pub contract: Option<String>,
-    pub ty: Type,
-    pub values: HashMap<String, (pt::Loc, usize)>,
-}
-
-impl EnumDecl {
-    /// Make the enum name into a string for printing. The enum can be declared either
-    /// inside or outside a contract.
-    pub fn print_to_string(&self) -> String {
-        match &self.contract {
-            Some(c) => format!("{}.{}", c, self.name),
-            None => self.name.to_owned(),
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct Parameter {
-    pub name: String,
-    pub ty: Type,
-}
-
-pub struct FunctionDecl {
-    pub doc: Vec<String>,
-    pub loc: pt::Loc,
-    pub name: String,
-    pub ty: pt::FunctionTy,
-    pub signature: String,
-    pub ast_index: Option<usize>,
-    pub mutability: Option<pt::StateMutability>,
-    pub visibility: pt::Visibility,
-    pub params: Vec<Parameter>,
-    pub returns: Vec<Parameter>,
-    pub noreturn: bool,
-    pub check_nonpayable: bool,
-    pub cfg: Option<Box<cfg::ControlFlowGraph>>,
-}
-
-impl FunctionDecl {
-    fn new(
-        loc: pt::Loc,
-        name: String,
-        doc: Vec<String>,
-        ty: pt::FunctionTy,
-        ast_index: Option<usize>,
-        mutability: Option<pt::StateMutability>,
-        visibility: pt::Visibility,
-        params: Vec<Parameter>,
-        returns: Vec<Parameter>,
-        ns: &Namespace,
-    ) -> Self {
-        let signature = format!(
-            "{}({})",
-            name,
-            params
-                .iter()
-                .map(|p| p.ty.to_signature_string(ns))
-                .collect::<Vec<String>>()
-                .join(",")
-        );
-
-        FunctionDecl {
-            doc,
-            loc,
-            name,
-            ty,
-            signature,
-            ast_index,
-            mutability,
-            visibility,
-            params,
-            returns,
-            noreturn: false,
-            check_nonpayable: false,
-            cfg: None,
-        }
-    }
-
-    /// Generate selector for this function
-    pub fn selector(&self) -> u32 {
-        let res = keccak256(self.signature.as_bytes());
-
-        u32::from_le_bytes([res[0], res[1], res[2], res[3]])
-    }
-
-    /// Is this a constructor
-    pub fn is_constructor(&self) -> bool {
-        self.ty == pt::FunctionTy::Constructor
-    }
-
-    /// Does this function have the payable state
-    pub fn is_payable(&self) -> bool {
-        if let Some(pt::StateMutability::Payable(_)) = self.mutability {
-            true
-        } else {
-            false
-        }
-    }
-
-    /// Is this function accessable externally
-    pub fn is_public(&self) -> bool {
-        match self.visibility {
-            pt::Visibility::Public(_) | pt::Visibility::External(_) => true,
-            _ => false,
-        }
-    }
-
-    /// Return a unique string for this function which is a valid wasm symbol
-    pub fn wasm_symbol(&self, ns: &Namespace) -> String {
-        let mut sig = self.name.to_owned();
-
-        if !self.params.is_empty() {
-            sig.push_str("__");
-
-            for (i, p) in self.params.iter().enumerate() {
-                if i > 0 {
-                    sig.push('_');
-                }
-
-                fn type_to_wasm_name(ty: &Type, ns: &Namespace) -> String {
-                    match ty {
-                        Type::Bool => "bool".to_string(),
-                        Type::Address(_) => "address".to_string(),
-                        Type::Int(n) => format!("int{}", n),
-                        Type::Uint(n) => format!("uint{}", n),
-                        Type::Bytes(n) => format!("bytes{}", n),
-                        Type::DynamicBytes => "bytes".to_string(),
-                        Type::String => "string".to_string(),
-                        Type::Enum(i) => ns.enums[*i].print_to_string(),
-                        Type::Struct(i) => ns.structs[*i].print_to_string(),
-                        Type::Array(ty, len) => format!(
-                            "{}{}",
-                            type_to_wasm_name(ty, ns),
-                            len.iter()
-                                .map(|r| match r {
-                                    None => ":".to_string(),
-                                    Some(r) => format!(":{}", r),
-                                })
-                                .collect::<String>()
-                        ),
-                        Type::Mapping(k, v) => format!(
-                            "mapping:{}:{}",
-                            type_to_wasm_name(k, ns),
-                            type_to_wasm_name(v, ns)
-                        ),
-                        Type::Contract(i) => ns.contracts[*i].name.to_owned(),
-                        Type::Undef => unreachable!(),
-                        Type::Ref(r) => type_to_wasm_name(r, ns),
-                        Type::StorageRef(r) => type_to_wasm_name(r, ns),
-                    }
-                }
-
-                sig.push_str(&type_to_wasm_name(&p.ty, ns));
-            }
-        }
-
-        sig
-    }
-}
-
-impl From<&pt::Type> for Type {
-    fn from(p: &pt::Type) -> Type {
-        match p {
-            pt::Type::Bool => Type::Bool,
-            pt::Type::Address => Type::Address(false),
-            pt::Type::AddressPayable => Type::Address(true),
-            pt::Type::Payable => Type::Address(true),
-            pt::Type::Int(n) => Type::Int(*n),
-            pt::Type::Uint(n) => Type::Uint(*n),
-            pt::Type::Bytes(n) => Type::Bytes(*n),
-            pt::Type::String => Type::String,
-            pt::Type::DynamicBytes => Type::DynamicBytes,
-            // needs special casing
-            pt::Type::Mapping(_, _, _) => unimplemented!(),
-        }
-    }
-}
-
-pub enum ContractVariableType {
-    Storage(BigInt),
-    Constant(usize),
-}
-
-pub struct ContractVariable {
-    pub doc: Vec<String>,
-    pub name: String,
-    pub ty: Type,
-    pub visibility: pt::Visibility,
-    pub var: ContractVariableType,
-}
-
-impl ContractVariable {
-    pub fn is_storage(&self) -> bool {
-        if let ContractVariableType::Storage(_) = self.var {
-            true
-        } else {
-            false
-        }
-    }
-}
-
-pub enum Symbol {
-    Enum(pt::Loc, usize),
-    Function(Vec<(pt::Loc, usize)>),
-    Variable(pt::Loc, usize),
-    Struct(pt::Loc, usize),
-    Contract(pt::Loc, usize),
-}
-
-/// When resolving a Solidity file, this holds all the resolved items
-pub struct Namespace {
-    pub target: Target,
-    pub enums: Vec<EnumDecl>,
-    pub structs: Vec<StructDecl>,
-    pub contracts: Vec<Contract>,
-    pub address_length: usize,
-    pub value_length: usize,
-    symbols: HashMap<(Option<usize>, String), Symbol>,
-}
-
-impl Namespace {
-    pub fn new(target: Target, address_length: usize) -> Self {
-        Namespace {
-            target,
-            enums: Vec::new(),
-            structs: Vec::new(),
-            contracts: Vec::new(),
-            address_length,
-            value_length: 16,
-            symbols: HashMap::new(),
-        }
-    }
-
-    /// Add symbol to symbol table; either returns true for success, or adds an appropriate error
-    pub fn add_symbol(
-        &mut self,
-        contract_no: Option<usize>,
-        id: &pt::Identifier,
-        symbol: Symbol,
-        errors: &mut Vec<Output>,
-    ) -> bool {
-        if let Some(sym) = self.symbols.get(&(contract_no, id.name.to_owned())) {
-            match sym {
-                Symbol::Contract(c, _) => {
-                    errors.push(Output::error_with_note(
-                        id.loc,
-                        format!(
-                            "{} is already defined as a contract name",
-                            id.name.to_string()
-                        ),
-                        *c,
-                        "location of previous definition".to_string(),
-                    ));
-                }
-                Symbol::Enum(c, _) => {
-                    errors.push(Output::error_with_note(
-                        id.loc,
-                        format!("{} is already defined as an enum", id.name.to_string()),
-                        *c,
-                        "location of previous definition".to_string(),
-                    ));
-                }
-                Symbol::Struct(c, _) => {
-                    errors.push(Output::error_with_note(
-                        id.loc,
-                        format!("{} is already defined as a struct", id.name.to_string()),
-                        *c,
-                        "location of previous definition".to_string(),
-                    ));
-                }
-                Symbol::Variable(c, _) => {
-                    errors.push(Output::error_with_note(
-                        id.loc,
-                        format!(
-                            "{} is already defined as a contract variable",
-                            id.name.to_string()
-                        ),
-                        *c,
-                        "location of previous definition".to_string(),
-                    ));
-                }
-                Symbol::Function(v) => {
-                    errors.push(Output::error_with_note(
-                        id.loc,
-                        format!("{} is already defined as a function", id.name.to_string()),
-                        v[0].0,
-                        "location of previous definition".to_string(),
-                    ));
-                }
-            }
-
-            return false;
-        }
-
-        // if there is nothing on the contract level, try top-level scope
-        if contract_no.is_some() {
-            if let Some(sym) = self.symbols.get(&(None, id.name.to_owned())) {
-                match sym {
-                    Symbol::Contract(c, _) => {
-                        errors.push(Output::warning_with_note(
-                            id.loc,
-                            format!(
-                                "{} is already defined as a contract name",
-                                id.name.to_string()
-                            ),
-                            *c,
-                            "location of previous definition".to_string(),
-                        ));
-                    }
-                    Symbol::Enum(c, _) => {
-                        errors.push(Output::warning_with_note(
-                            id.loc,
-                            format!("{} is already defined as an enum", id.name.to_string()),
-                            *c,
-                            "location of previous definition".to_string(),
-                        ));
-                    }
-                    Symbol::Struct(c, _) => {
-                        errors.push(Output::warning_with_note(
-                            id.loc,
-                            format!("{} is already defined as a struct", id.name.to_string()),
-                            *c,
-                            "location of previous definition".to_string(),
-                        ));
-                    }
-                    Symbol::Variable(c, _) => {
-                        errors.push(Output::warning_with_note(
-                            id.loc,
-                            format!(
-                                "{} is already defined as a contract variable",
-                                id.name.to_string()
-                            ),
-                            *c,
-                            "location of previous definition".to_string(),
-                        ));
-                    }
-                    Symbol::Function(v) => {
-                        errors.push(Output::warning_with_note(
-                            id.loc,
-                            format!("{} is already defined as a function", id.name.to_string()),
-                            v[0].0,
-                            "location of previous definition".to_string(),
-                        ));
-                    }
-                }
-            }
-        }
-
-        self.symbols
-            .insert((contract_no, id.name.to_string()), symbol);
-
-        true
-    }
-
-    pub fn resolve_enum(&self, contract_no: Option<usize>, id: &pt::Identifier) -> Option<usize> {
-        if let Some(Symbol::Enum(_, n)) = self.symbols.get(&(contract_no, id.name.to_owned())) {
-            return Some(*n);
-        }
-
-        if contract_no.is_some() {
-            if let Some(Symbol::Enum(_, n)) = self.symbols.get(&(None, id.name.to_owned())) {
-                return Some(*n);
-            }
-        }
-
-        None
-    }
-
-    pub fn resolve_contract(&self, id: &pt::Identifier) -> Option<usize> {
-        if let Some(Symbol::Contract(_, n)) = self.symbols.get(&(None, id.name.to_owned())) {
-            return Some(*n);
-        }
-
-        None
-    }
-
-    pub fn resolve_func(
-        &self,
-        contract_no: usize,
-        id: &pt::Identifier,
-        errors: &mut Vec<Output>,
-    ) -> Result<&Vec<(pt::Loc, usize)>, ()> {
-        match self.symbols.get(&(Some(contract_no), id.name.to_owned())) {
-            Some(Symbol::Function(v)) => Ok(v),
-            _ => {
-                errors.push(Output::error(
-                    id.loc,
-                    "unknown function or type".to_string(),
-                ));
-
-                Err(())
-            }
-        }
-    }
-
-    pub fn resolve_var(
-        &self,
-        contract_no: usize,
-        id: &pt::Identifier,
-        errors: &mut Vec<Output>,
-    ) -> Result<usize, ()> {
-        let mut s = self.symbols.get(&(Some(contract_no), id.name.to_owned()));
-
-        if s.is_none() {
-            s = self.symbols.get(&(None, id.name.to_owned()));
-        }
-
-        match s {
-            None => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("`{}' is not declared", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Enum(_, _)) => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("`{}' is an enum", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Struct(_, _)) => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("`{}' is a struct", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Function(_)) => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("`{}' is a function", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Contract(_, _)) => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("`{}' is a contract", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Variable(_, n)) => Ok(*n),
-        }
-    }
-
-    pub fn check_shadowing(
-        &self,
-        contract_no: usize,
-        id: &pt::Identifier,
-        errors: &mut Vec<Output>,
-    ) {
-        let mut s = self.symbols.get(&(Some(contract_no), id.name.to_owned()));
-
-        if s.is_none() {
-            s = self.symbols.get(&(None, id.name.to_owned()));
-        }
-
-        match s {
-            Some(Symbol::Enum(loc, _)) => {
-                errors.push(Output::warning_with_note(
-                    id.loc,
-                    format!("declaration of `{}' shadows enum definition", id.name),
-                    *loc,
-                    "previous definition of enum".to_string(),
-                ));
-            }
-            Some(Symbol::Struct(loc, _)) => {
-                errors.push(Output::warning_with_note(
-                    id.loc,
-                    format!("declaration of `{}' shadows struct definition", id.name),
-                    *loc,
-                    "previous definition of struct".to_string(),
-                ));
-            }
-            Some(Symbol::Function(v)) => {
-                let notes = v
-                    .iter()
-                    .map(|(pos, _)| Note {
-                        pos: *pos,
-                        message: "previous declaration of function".to_owned(),
-                    })
-                    .collect();
-                errors.push(Output::warning_with_notes(
-                    id.loc,
-                    format!("declaration of `{}' shadows function", id.name),
-                    notes,
-                ));
-            }
-            Some(Symbol::Variable(loc, _)) => {
-                errors.push(Output::warning_with_note(
-                    id.loc,
-                    format!("declaration of `{}' shadows state variable", id.name),
-                    *loc,
-                    "previous declaration of state variable".to_string(),
-                ));
-            }
-            Some(Symbol::Contract(loc, _)) => {
-                errors.push(Output::warning_with_note(
-                    id.loc,
-                    format!("declaration of `{}' shadows contract name", id.name),
-                    *loc,
-                    "previous declaration of contract name".to_string(),
-                ));
-            }
-            None => {}
-        }
-    }
-
-    /// Resolve the parsed data type. The type can be a primitive, enum and also an arrays.
-    /// The type for address payable is "address payble" used as a type, and "payable" when
-    /// casting. So, we need to know what we are resolving for.
-    pub fn resolve_type(
-        &self,
-        contract_no: Option<usize>,
-        casting: bool,
-        id: &pt::Expression,
-        errors: &mut Vec<Output>,
-    ) -> Result<Type, ()> {
-        fn resolve_dimensions(
-            ast_dimensions: &[Option<(pt::Loc, BigInt)>],
-            errors: &mut Vec<Output>,
-        ) -> Result<Vec<Option<BigInt>>, ()> {
-            let mut dimensions = Vec::new();
-
-            for d in ast_dimensions.iter().rev() {
-                if let Some((loc, n)) = d {
-                    if n.is_zero() {
-                        errors.push(Output::decl_error(
-                            *loc,
-                            "zero size array not permitted".to_string(),
-                        ));
-                        return Err(());
-                    } else if n.is_negative() {
-                        errors.push(Output::decl_error(
-                            *loc,
-                            "negative size of array declared".to_string(),
-                        ));
-                        return Err(());
-                    }
-                    dimensions.push(Some(n.clone()));
-                } else {
-                    dimensions.push(None);
-                }
-            }
-
-            Ok(dimensions)
-        }
-
-        let (contract_name, id, dimensions) = self.expr_to_type(&id, errors)?;
-
-        if let pt::Expression::Type(_, ty) = &id {
-            assert_eq!(contract_name, None);
-
-            let ty = match ty {
-                pt::Type::Mapping(_, k, v) => {
-                    let key = self.resolve_type(contract_no, false, k, errors)?;
-                    let value = self.resolve_type(contract_no, false, v, errors)?;
-
-                    match key {
-                        Type::Mapping(_, _) => {
-                            errors.push(Output::decl_error(
-                                k.loc(),
-                                "key of mapping cannot be another mapping type".to_string(),
-                            ));
-                            return Err(());
-                        }
-                        Type::Struct(_) => {
-                            errors.push(Output::decl_error(
-                                k.loc(),
-                                "key of mapping cannot be struct type".to_string(),
-                            ));
-                            return Err(());
-                        }
-                        Type::Array(_, _) => {
-                            errors.push(Output::decl_error(
-                                k.loc(),
-                                "key of mapping cannot be array type".to_string(),
-                            ));
-                            return Err(());
-                        }
-                        _ => Type::Mapping(Box::new(key), Box::new(value)),
-                    }
-                }
-                pt::Type::Payable => {
-                    if !casting {
-                        errors.push(Output::decl_error(
-                            id.loc(),
-                            "‘payable’ cannot be used for type declarations, only casting. use ‘address payable’"
-                                .to_string(),
-                        ));
-                        return Err(());
-                    } else {
-                        Type::Address(true)
-                    }
-                }
-                _ => Type::from(ty),
-            };
-
-            return if dimensions.is_empty() {
-                Ok(ty)
-            } else {
-                Ok(Type::Array(
-                    Box::new(ty),
-                    resolve_dimensions(&dimensions, errors)?,
-                ))
-            };
-        }
-
-        let id = match id {
-            pt::Expression::Variable(id) => id,
-            _ => unreachable!(),
-        };
-
-        let contract_no = if let Some(contract_name) = contract_name {
-            match self.symbols.get(&(None, contract_name.name)) {
-                None => {
-                    errors.push(Output::decl_error(
-                        id.loc,
-                        format!("contract type ‘{}’ not found", id.name),
-                    ));
-                    return Err(());
-                }
-                Some(Symbol::Contract(_, n)) => Some(*n),
-                Some(Symbol::Function(_)) => {
-                    errors.push(Output::decl_error(
-                        id.loc,
-                        format!("‘{}’ is a function", id.name),
-                    ));
-                    return Err(());
-                }
-                Some(Symbol::Variable(_, _)) => {
-                    errors.push(Output::decl_error(
-                        id.loc,
-                        format!("‘{}’ is a contract variable", id.name),
-                    ));
-                    return Err(());
-                }
-                Some(Symbol::Struct(_, _)) => {
-                    errors.push(Output::decl_error(
-                        id.loc,
-                        format!("‘{}’ is a struct", id.name),
-                    ));
-                    return Err(());
-                }
-                Some(Symbol::Enum(_, _)) => {
-                    errors.push(Output::decl_error(
-                        id.loc,
-                        format!("‘{}’ is an enum variable", id.name),
-                    ));
-                    return Err(());
-                }
-            }
-        } else {
-            contract_no
-        };
-
-        let mut s = self.symbols.get(&(contract_no, id.name.to_owned()));
-
-        // try global scope
-        if s.is_none() && contract_no.is_some() {
-            s = self.symbols.get(&(None, id.name.to_owned()));
-        }
-
-        match s {
-            None => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("type ‘{}’ not found", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Enum(_, n)) if dimensions.is_empty() => Ok(Type::Enum(*n)),
-            Some(Symbol::Enum(_, n)) => Ok(Type::Array(
-                Box::new(Type::Enum(*n)),
-                resolve_dimensions(&dimensions, errors)?,
-            )),
-            Some(Symbol::Struct(_, n)) if dimensions.is_empty() => Ok(Type::Struct(*n)),
-            Some(Symbol::Struct(_, n)) => Ok(Type::Array(
-                Box::new(Type::Struct(*n)),
-                resolve_dimensions(&dimensions, errors)?,
-            )),
-            Some(Symbol::Contract(_, n)) if dimensions.is_empty() => Ok(Type::Contract(*n)),
-            Some(Symbol::Contract(_, n)) => Ok(Type::Array(
-                Box::new(Type::Contract(*n)),
-                resolve_dimensions(&dimensions, errors)?,
-            )),
-            Some(Symbol::Function(_)) => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("‘{}’ is a function", id.name),
-                ));
-                Err(())
-            }
-            Some(Symbol::Variable(_, _)) => {
-                errors.push(Output::decl_error(
-                    id.loc,
-                    format!("‘{}’ is a contract variable", id.name),
-                ));
-                Err(())
-            }
-        }
-    }
-
-    // An array type can look like foo[2], if foo is an enum type. The lalrpop parses
-    // this as an expression, so we need to convert it to Type and check there are
-    // no unexpected expressions types.
-    pub fn expr_to_type(
-        &self,
-        expr: &pt::Expression,
-        errors: &mut Vec<Output>,
-    ) -> Result<(Option<pt::Identifier>, pt::Expression, Vec<ArrayDimension>), ()> {
-        let mut expr = expr;
-        let mut dimensions = Vec::new();
-
-        loop {
-            expr = match expr {
-                pt::Expression::ArraySubscript(_, r, None) => {
-                    dimensions.push(None);
-
-                    &*r
-                }
-                pt::Expression::ArraySubscript(_, r, Some(index)) => {
-                    dimensions.push(self.resolve_array_dimension(index, errors)?);
-
-                    &*r
-                }
-                pt::Expression::Variable(_) | pt::Expression::Type(_, _) => {
-                    return Ok((None, expr.clone(), dimensions))
-                }
-                pt::Expression::MemberAccess(_, namespace, id) => {
-                    if let pt::Expression::Variable(namespace) = namespace.as_ref() {
-                        return Ok((
-                            Some(namespace.clone()),
-                            pt::Expression::Variable(id.clone()),
-                            dimensions,
-                        ));
-                    } else {
-                        errors.push(Output::decl_error(
-                            namespace.loc(),
-                            "expression found where contract type expected".to_string(),
-                        ));
-                        return Err(());
-                    }
-                }
-                _ => {
-                    errors.push(Output::decl_error(
-                        expr.loc(),
-                        "expression found where type expected".to_string(),
-                    ));
-                    return Err(());
-                }
-            }
-        }
-    }
-
-    /// Resolve an expression which defines the array length, e.g. 2**8 in "bool[2**8]"
-    pub fn resolve_array_dimension(
-        &self,
-        expr: &pt::Expression,
-        errors: &mut Vec<Output>,
-    ) -> Result<ArrayDimension, ()> {
-        let mut cfg = ControlFlowGraph::new();
-        let (size_expr, size_ty) = expression(&expr, &mut cfg, None, self, &mut None, errors)?;
-        match size_ty {
-            Type::Uint(_) | Type::Int(_) => {}
-            _ => {
-                errors.push(Output::decl_error(
-                    expr.loc(),
-                    "expression is not a number".to_string(),
-                ));
-                return Err(());
-            }
-        }
-        Ok(Some(eval_number_expression(&size_expr, errors)?))
-    }
-
-    pub fn abi(&self, contract_no: usize, verbose: bool) -> (String, &'static str) {
-        abi::generate_abi(contract_no, self, verbose)
-    }
-}
-
-pub struct Contract {
-    pub doc: Vec<String>,
-    pub name: String,
-    // events
-    pub functions: Vec<FunctionDecl>,
-    pub variables: Vec<ContractVariable>,
-    pub constants: Vec<Expression>,
-    pub initializer: cfg::ControlFlowGraph,
-    top_of_contract_storage: BigInt,
-    creates: RefCell<Vec<usize>>,
-}
-
-impl Contract {
-    pub fn new(name: &str) -> Self {
-        Contract {
-            name: name.to_owned(),
-            doc: Vec::new(),
-            functions: Vec::new(),
-            variables: Vec::new(),
-            constants: Vec::new(),
-            initializer: cfg::ControlFlowGraph::new(),
-            top_of_contract_storage: BigInt::zero(),
-            creates: RefCell::new(Vec::new()),
-        }
-    }
-
-    /// Return the index of the fallback function, if any
-    pub fn fallback_function(&self) -> Option<usize> {
-        for (i, f) in self.functions.iter().enumerate() {
-            if f.ty == pt::FunctionTy::Fallback {
-                return Some(i);
-            }
-        }
-        None
-    }
-
-    /// Return the index of the receive function, if any
-    pub fn receive_function(&self) -> Option<usize> {
-        for (i, f) in self.functions.iter().enumerate() {
-            if f.ty == pt::FunctionTy::Receive {
-                return Some(i);
-            }
-        }
-        None
-    }
-
-    pub fn emit<'a>(
-        &'a self,
-        ns: &'a Namespace,
-        context: &'a inkwell::context::Context,
-        filename: &'a str,
-        opt: OptimizationLevel,
-    ) -> emit::Contract {
-        emit::Contract::build(context, self, ns, filename, opt)
-    }
-
-    /// Print the entire contract; storage initializers, constructors and functions and their CFGs
-    pub fn print_to_string(&self, ns: &Namespace) -> String {
-        let mut out = format!("#\n# Contract: {}\n#\n\n", self.name);
-
-        out += "# storage initializer\n";
-        out += &self.initializer.to_string(self, ns);
-
-        for func in self.functions.iter() {
-            out += &format!("\n# {} {}\n", func.ty, func.signature);
-
-            if let Some(ref cfg) = func.cfg {
-                out += &cfg.to_string(self, ns);
-            }
-        }
-
-        out
-    }
-}
-
-pub fn resolver(s: pt::SourceUnit, target: Target) -> (Option<Namespace>, Vec<Output>) {
-    // first resolve all the types we can find
-    let (mut ns, mut errors) = types::resolve(&s, target);
-
-    // give up if we failed
-    if any_errors(&errors) {
-        return (None, errors);
-    }
-
-    // we need to resolve declarations first, so we call functions/constructors of
-    // contracts before they are declared
-    let mut contract_no = 0;
-    for part in &s.0 {
-        if let pt::SourceUnitPart::ContractDefinition(def) = part {
-            resolve_contract_declarations(def, contract_no, target, &mut errors, &mut ns);
-
-            contract_no += 1;
-        }
-    }
-
-    // Now we can resolve the bodies
-    let mut contract_no = 0;
-    for part in &s.0 {
-        if let pt::SourceUnitPart::ContractDefinition(def) = part {
-            resolve_contract_bodies(def, contract_no, &mut errors, &mut ns);
-
-            contract_no += 1;
-        }
-    }
-
-    if any_errors(&errors) {
-        (None, errors)
-    } else {
-        (Some(ns), errors)
-    }
-}
-
-/// Resolve functions declarations, constructor declarations, and contract variables
-fn resolve_contract_declarations(
-    def: &pt::ContractDefinition,
-    contract_no: usize,
-    target: Target,
-    errors: &mut Vec<Output>,
-    ns: &mut Namespace,
-) -> bool {
-    errors.push(Output::info(
-        def.loc,
-        format!("found contract {}", def.name.name),
-    ));
-
-    builtin::add_builtin_function(ns, contract_no);
-
-    let mut broken = false;
-
-    // resolve function signatures
-    for (i, parts) in def.parts.iter().enumerate() {
-        if let pt::ContractPart::FunctionDefinition(ref f) = parts {
-            if !functions::function_decl(f, i, contract_no, ns, errors) {
-                broken = true;
-            }
-        }
-    }
-
-    // resolve state variables
-    if variables::contract_variables(&def, contract_no, ns, errors) {
-        broken = true;
-    }
-
-    // Substrate requires one constructor
-    if !ns.contracts[contract_no]
-        .functions
-        .iter()
-        .any(|f| f.is_constructor())
-        && target == Target::Substrate
-    {
-        let mut fdecl = FunctionDecl::new(
-            pt::Loc(0, 0),
-            "".to_owned(),
-            vec![],
-            pt::FunctionTy::Constructor,
-            None,
-            None,
-            pt::Visibility::Public(pt::Loc(0, 0)),
-            Vec::new(),
-            Vec::new(),
-            ns,
-        );
-
-        let mut vartab = Vartable::new();
-        let mut cfg = ControlFlowGraph::new();
-
-        cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
-        cfg.vars = vartab.drain();
-
-        fdecl.cfg = Some(Box::new(cfg));
-
-        ns.contracts[contract_no].functions.push(fdecl);
-    }
-
-    broken
-}
-
-fn resolve_contract_bodies(
-    def: &pt::ContractDefinition,
-    contract_no: usize,
-    errors: &mut Vec<Output>,
-    ns: &mut Namespace,
-) -> bool {
-    let mut broken = false;
-
-    // resolve function bodies
-    for f in 0..ns.contracts[contract_no].functions.len() {
-        if let Some(ast_index) = ns.contracts[contract_no].functions[f].ast_index {
-            if let pt::ContractPart::FunctionDefinition(ref ast_f) = def.parts[ast_index] {
-                match cfg::generate_cfg(
-                    ast_f,
-                    &ns.contracts[contract_no].functions[f],
-                    contract_no,
-                    &ns,
-                    errors,
-                ) {
-                    Ok(c) => {
-                        match &ns.contracts[contract_no].functions[f].mutability {
-                            Some(pt::StateMutability::Pure(loc)) => {
-                                if c.writes_contract_storage {
-                                    errors.push(Output::error(
-                                        *loc,
-                                        "function declared pure but writes contract storage"
-                                            .to_string(),
-                                    ));
-                                    broken = true;
-                                } else if c.reads_contract_storage() {
-                                    errors.push(Output::error(
-                                        *loc,
-                                        "function declared pure but reads contract storage"
-                                            .to_string(),
-                                    ));
-                                    broken = true;
-                                }
-                            }
-                            Some(pt::StateMutability::View(loc)) => {
-                                if c.writes_contract_storage {
-                                    errors.push(Output::error(
-                                        *loc,
-                                        "function declared view but writes contract storage"
-                                            .to_string(),
-                                    ));
-                                    broken = true;
-                                } else if !c.reads_contract_storage() {
-                                    errors.push(Output::warning(
-                                        *loc,
-                                        "function can be declared pure".to_string(),
-                                    ));
-                                }
-                            }
-                            Some(pt::StateMutability::Payable(_)) => {
-                                //
-                            }
-                            None => {
-                                let loc = &ns.contracts[contract_no].functions[f].loc;
-
-                                if !c.writes_contract_storage && !c.reads_contract_storage() {
-                                    errors.push(Output::warning(
-                                        *loc,
-                                        "function can be declare pure".to_string(),
-                                    ));
-                                } else if !c.writes_contract_storage {
-                                    errors.push(Output::warning(
-                                        *loc,
-                                        "function can be declared view".to_string(),
-                                    ));
-                                }
-                            }
-                        }
-                        ns.contracts[contract_no].functions[f].cfg = Some(c);
-                    }
-                    Err(_) => broken = true,
-                }
-            }
-        }
-    }
-
-    broken
-}

+ 0 - 493
src/resolver/storage.rs

@@ -1,493 +0,0 @@
-use num_bigint::BigInt;
-use num_traits::FromPrimitive;
-use num_traits::One;
-use num_traits::Zero;
-
-use super::cfg::{ControlFlowGraph, Instr, Vartable};
-use super::expression::{cast, expression, Expression};
-use output::Output;
-use parser::pt;
-use resolver;
-
-/// Given a storage slot which is the start of the array, calculate the
-/// offset of the array element. This function exists to avoid doing
-/// 256 bit multiply if possible.
-pub fn array_offset(
-    loc: &pt::Loc,
-    start: Expression,
-    index: Expression,
-    elem_ty: resolver::Type,
-    ns: &resolver::Namespace,
-) -> Expression {
-    let elem_size = elem_ty.storage_slots(ns);
-
-    // the index needs to be cast to i256 and multiplied by the number
-    // of slots for each element
-    if elem_size == BigInt::one() {
-        Expression::Add(*loc, Box::new(start), Box::new(index))
-    } else if (elem_size.clone() & (elem_size.clone() - BigInt::one())) == BigInt::zero() {
-        // elem_size is power of 2
-        Expression::ShiftLeft(
-            *loc,
-            Box::new(start),
-            Box::new(Expression::ShiftLeft(
-                *loc,
-                Box::new(index),
-                Box::new(Expression::NumberLiteral(
-                    *loc,
-                    256,
-                    BigInt::from_usize(elem_size.bits()).unwrap(),
-                )),
-            )),
-        )
-    } else {
-        Expression::Add(
-            *loc,
-            Box::new(start),
-            Box::new(Expression::Multiply(
-                *loc,
-                Box::new(index),
-                Box::new(Expression::NumberLiteral(*loc, 256, elem_size)),
-            )),
-        )
-    }
-}
-
-/// Resolve delete statement
-pub fn delete(
-    loc: &pt::Loc,
-    var: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (var_expr, var_ty) = expression(var, cfg, contract_no, ns, vartab, errors)?;
-
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                "cannot use ‘delete’ in constant expression".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    if let resolver::Type::StorageRef(ty) = &var_ty {
-        if ty.is_mapping() {
-            errors.push(Output::error(
-                *loc,
-                "‘delete’ cannot be applied to mapping type".to_string(),
-            ));
-            return Err(());
-        }
-
-        cfg.writes_contract_storage = true;
-        cfg.add(
-            tab,
-            Instr::ClearStorage {
-                ty: ty.as_ref().clone(),
-                storage: var_expr,
-            },
-        );
-    } else {
-        errors.push(Output::error(
-            *loc,
-            "argument to ‘delete’ should be storage reference".to_string(),
-        ));
-        return Err(());
-    }
-
-    Ok((Expression::Poison, resolver::Type::Undef))
-}
-
-/// Push() method on dynamic array in storage
-pub fn array_push(
-    loc: &pt::Loc,
-    var_expr: Expression,
-    func: &pt::Identifier,
-    ty: &resolver::Type,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                format!("cannot call method ‘{}’ in constant expression", func.name),
-            ));
-            return Err(());
-        }
-    };
-
-    if args.len() > 1 {
-        errors.push(Output::error(
-            func.loc,
-            "method ‘push()’ takes at most 1 argument".to_string(),
-        ));
-        return Err(());
-    }
-
-    // set array+length to val_expr
-    let slot_ty = resolver::Type::Uint(256);
-    let length_pos = tab.temp_anonymous(&slot_ty);
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: length_pos,
-            expr: Expression::StorageLoad(*loc, slot_ty.clone(), Box::new(var_expr.clone())),
-        },
-    );
-
-    let elem_ty = ty.storage_deref();
-
-    let entry_pos = tab.temp_anonymous(&slot_ty);
-
-    cfg.writes_contract_storage = true;
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: entry_pos,
-            expr: array_offset(
-                loc,
-                Expression::Keccak256(*loc, vec![(var_expr.clone(), slot_ty.clone())]),
-                Expression::Variable(*loc, length_pos),
-                elem_ty.clone(),
-                ns,
-            ),
-        },
-    );
-
-    if args.len() == 1 {
-        let (val_expr, val_ty) =
-            expression(&args[0], cfg, contract_no, ns, &mut Some(tab), errors)?;
-
-        let pos = tab.temp_anonymous(&elem_ty);
-
-        cfg.add(
-            tab,
-            Instr::Set {
-                res: pos,
-                expr: cast(
-                    &args[0].loc(),
-                    val_expr,
-                    &val_ty,
-                    &elem_ty.deref(),
-                    true,
-                    ns,
-                    errors,
-                )?,
-            },
-        );
-
-        cfg.add(
-            tab,
-            Instr::SetStorage {
-                ty: elem_ty.clone(),
-                local: pos,
-                storage: Expression::Variable(*loc, entry_pos),
-            },
-        );
-    }
-
-    // increase length
-    let new_length = tab.temp_anonymous(&slot_ty);
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: new_length,
-            expr: Expression::Add(
-                *loc,
-                Box::new(Expression::Variable(*loc, length_pos)),
-                Box::new(Expression::NumberLiteral(*loc, 256, BigInt::one())),
-            ),
-        },
-    );
-
-    cfg.add(
-        tab,
-        Instr::SetStorage {
-            ty: slot_ty,
-            local: new_length,
-            storage: var_expr,
-        },
-    );
-
-    if args.is_empty() {
-        Ok((Expression::Variable(*loc, entry_pos), elem_ty))
-    } else {
-        Ok((Expression::Poison, resolver::Type::Undef))
-    }
-}
-
-/// Pop() method on dynamic array in storage
-pub fn array_pop(
-    loc: &pt::Loc,
-    var_expr: Expression,
-    func: &pt::Identifier,
-    ty: &resolver::Type,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                format!("cannot call method ‘{}’ in constant expression", func.name),
-            ));
-            return Err(());
-        }
-    };
-
-    if !args.is_empty() {
-        errors.push(Output::error(
-            func.loc,
-            "method ‘pop()’ does not take any arguments".to_string(),
-        ));
-        return Err(());
-    }
-
-    // set array+length to val_expr
-    let slot_ty = resolver::Type::Uint(256);
-    let length_pos = tab.temp_anonymous(&slot_ty);
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: length_pos,
-            expr: Expression::StorageLoad(*loc, slot_ty.clone(), Box::new(var_expr.clone())),
-        },
-    );
-
-    let empty_array = cfg.new_basic_block("empty_array".to_string());
-    let has_elements = cfg.new_basic_block("has_elements".to_string());
-
-    cfg.writes_contract_storage = true;
-    cfg.add(
-        tab,
-        Instr::BranchCond {
-            cond: Expression::Equal(
-                *loc,
-                Box::new(Expression::Variable(*loc, length_pos)),
-                Box::new(Expression::NumberLiteral(*loc, 256, BigInt::zero())),
-            ),
-            true_: empty_array,
-            false_: has_elements,
-        },
-    );
-
-    cfg.set_basic_block(empty_array);
-    cfg.add(tab, Instr::AssertFailure { expr: None });
-
-    cfg.set_basic_block(has_elements);
-    let new_length = tab.temp_anonymous(&slot_ty);
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: new_length,
-            expr: Expression::Subtract(
-                *loc,
-                Box::new(Expression::Variable(*loc, length_pos)),
-                Box::new(Expression::NumberLiteral(*loc, 256, BigInt::one())),
-            ),
-        },
-    );
-
-    // The array element will be loaded before clearing. So, the return
-    // type of pop() is the derefenced array dereference
-    let elem_ty = ty.storage_deref().deref().clone();
-    let entry_pos = tab.temp_anonymous(&slot_ty);
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: entry_pos,
-            expr: array_offset(
-                loc,
-                Expression::Keccak256(*loc, vec![(var_expr.clone(), slot_ty.clone())]),
-                Expression::Variable(*loc, new_length),
-                elem_ty.clone(),
-                ns,
-            ),
-        },
-    );
-
-    let res_pos = tab.temp_anonymous(&elem_ty);
-
-    cfg.add(
-        tab,
-        Instr::Set {
-            res: res_pos,
-            expr: Expression::StorageLoad(
-                *loc,
-                elem_ty.clone(),
-                Box::new(Expression::Variable(*loc, entry_pos)),
-            ),
-        },
-    );
-
-    cfg.add(
-        tab,
-        Instr::ClearStorage {
-            ty: elem_ty.clone(),
-            storage: Expression::Variable(*loc, entry_pos),
-        },
-    );
-
-    // set decrease length
-    cfg.add(
-        tab,
-        Instr::SetStorage {
-            ty: slot_ty,
-            local: new_length,
-            storage: var_expr,
-        },
-    );
-
-    Ok((Expression::Variable(*loc, res_pos), elem_ty))
-}
-
-/// Push() method on dynamic bytes in storage
-pub fn bytes_push(
-    loc: &pt::Loc,
-    var_expr: Expression,
-    func: &pt::Identifier,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let tab = match vartab {
-        &mut Some(ref mut tab) => tab,
-        None => {
-            errors.push(Output::error(
-                *loc,
-                format!("cannot call method ‘{}’ in constant expression", func.name),
-            ));
-            return Err(());
-        }
-    };
-
-    cfg.writes_contract_storage = true;
-
-    let val = match args.len() {
-        0 => Expression::NumberLiteral(*loc, 8, BigInt::zero()),
-        1 => {
-            let (val_expr, val_ty) =
-                expression(&args[0], cfg, contract_no, ns, &mut Some(tab), errors)?;
-
-            cast(
-                &args[0].loc(),
-                val_expr,
-                &val_ty,
-                &resolver::Type::Bytes(1),
-                true,
-                ns,
-                errors,
-            )?
-        }
-        _ => {
-            errors.push(Output::error(
-                func.loc,
-                "method ‘push()’ takes at most 1 argument".to_string(),
-            ));
-            return Err(());
-        }
-    };
-
-    if args.is_empty() {
-        Ok((
-            Expression::StorageBytesPush(*loc, Box::new(var_expr), Box::new(val)),
-            resolver::Type::Bytes(1),
-        ))
-    } else {
-        Ok((
-            Expression::StorageBytesPush(*loc, Box::new(var_expr), Box::new(val)),
-            resolver::Type::Undef,
-        ))
-    }
-}
-
-/// Pop() method on dynamic bytes in storage
-pub fn bytes_pop(
-    loc: &pt::Loc,
-    var_expr: Expression,
-    func: &pt::Identifier,
-    args: &[pt::Expression],
-    cfg: &mut ControlFlowGraph,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    cfg.writes_contract_storage = true;
-
-    if !args.is_empty() {
-        errors.push(Output::error(
-            func.loc,
-            "method ‘pop()’ does not take any arguments".to_string(),
-        ));
-        return Err(());
-    }
-
-    Ok((
-        Expression::StorageBytesPop(*loc, Box::new(var_expr)),
-        resolver::Type::Bytes(1),
-    ))
-}
-
-/// Calculate storage subscript
-pub fn mapping_subscript(
-    loc: &pt::Loc,
-    mapping: Expression,
-    mapping_ty: &resolver::Type,
-    index: &pt::Expression,
-    cfg: &mut ControlFlowGraph,
-    contract_no: Option<usize>,
-    ns: &resolver::Namespace,
-    vartab: &mut Option<&mut Vartable>,
-    errors: &mut Vec<Output>,
-) -> Result<(Expression, resolver::Type), ()> {
-    let (key_ty, value_ty) = match mapping_ty.deref() {
-        resolver::Type::Mapping(k, v) => (k, v),
-        _ => unreachable!(),
-    };
-
-    let (index_expr, index_ty) = expression(index, cfg, contract_no, ns, vartab, errors)?;
-
-    let index_expr = cast(
-        &index.loc(),
-        index_expr,
-        &index_ty,
-        key_ty,
-        true,
-        ns,
-        errors,
-    )?;
-
-    let slot_ty = resolver::Type::Uint(256);
-
-    let index_ty = if let resolver::Type::Enum(n) = index_ty {
-        ns.enums[n].ty.clone()
-    } else {
-        index_ty
-    };
-
-    let slot = Expression::Keccak256(*loc, vec![(mapping, slot_ty), (index_expr, index_ty)]);
-
-    Ok((slot, resolver::Type::StorageRef(value_ty.clone())))
-}

+ 0 - 0
src/resolver/address.rs → src/sema/address.rs


+ 818 - 0
src/sema/ast.rs

@@ -0,0 +1,818 @@
+use codegen::cfg::ControlFlowGraph;
+use num_bigint::BigInt;
+use num_traits::One;
+use output;
+use parser::pt;
+use sema::symtable::Symtable;
+use std::collections::HashMap;
+use std::ops::Mul;
+use tiny_keccak::keccak256;
+use Target;
+
+#[derive(PartialEq, Clone, Debug)]
+pub enum Type {
+    Address(bool),
+    Bool,
+    Int(u16),
+    Uint(u16),
+    Bytes(u8),
+    DynamicBytes,
+    String,
+    Array(Box<Type>, Vec<Option<BigInt>>),
+    Enum(usize),
+    Struct(usize),
+    Mapping(Box<Type>, Box<Type>),
+    Contract(usize),
+    Ref(Box<Type>),
+    StorageRef(Box<Type>),
+    Void,
+    Unreachable,
+}
+
+impl Type {
+    pub fn to_string(&self, ns: &Namespace) -> String {
+        match self {
+            Type::Bool => "bool".to_string(),
+            Type::Address(false) => "address".to_string(),
+            Type::Address(true) => "address payable".to_string(),
+            Type::Int(n) => format!("int{}", n),
+            Type::Uint(n) => format!("uint{}", n),
+            Type::Bytes(n) => format!("bytes{}", n),
+            Type::String => "string".to_string(),
+            Type::DynamicBytes => "bytes".to_string(),
+            Type::Enum(n) => format!("enum {}", ns.enums[*n].print_to_string()),
+            Type::Struct(n) => format!("struct {}", ns.structs[*n].print_to_string()),
+            Type::Array(ty, len) => format!(
+                "{}{}",
+                ty.to_string(ns),
+                len.iter()
+                    .map(|l| match l {
+                        None => "[]".to_string(),
+                        Some(l) => format!("[{}]", l),
+                    })
+                    .collect::<String>()
+            ),
+            Type::Mapping(k, v) => format!("mapping({} => {})", k.to_string(ns), v.to_string(ns)),
+            Type::Contract(n) => format!("contract {}", ns.contracts[*n].name),
+            Type::Ref(r) => r.to_string(ns),
+            Type::StorageRef(ty) => format!("{} storage", ty.to_string(ns)),
+            Type::Void => "void".to_owned(),
+            Type::Unreachable => "unreachable".to_owned(),
+        }
+    }
+
+    /// Is this a primitive, i.e. bool, address, int, uint, bytes
+    pub fn is_primitive(&self) -> bool {
+        match self {
+            Type::Bool => true,
+            Type::Address(_) => true,
+            Type::Int(_) => true,
+            Type::Uint(_) => true,
+            Type::Bytes(_) => true,
+            Type::Ref(r) => r.is_primitive(),
+            Type::StorageRef(r) => r.is_primitive(),
+            _ => false,
+        }
+    }
+
+    pub fn to_signature_string(&self, ns: &Namespace) -> String {
+        match self {
+            Type::Bool => "bool".to_string(),
+            Type::Contract(_) | Type::Address(_) => "address".to_string(),
+            Type::Int(n) => format!("int{}", n),
+            Type::Uint(n) => format!("uint{}", n),
+            Type::Bytes(n) => format!("bytes{}", n),
+            Type::DynamicBytes => "bytes".to_string(),
+            Type::String => "string".to_string(),
+            Type::Enum(n) => ns.enums[*n].ty.to_signature_string(ns),
+            Type::Array(ty, len) => format!(
+                "{}{}",
+                ty.to_signature_string(ns),
+                len.iter()
+                    .map(|l| match l {
+                        None => "[]".to_string(),
+                        Some(l) => format!("[{}]", l),
+                    })
+                    .collect::<String>()
+            ),
+            Type::Ref(r) => r.to_string(ns),
+            Type::StorageRef(r) => r.to_string(ns),
+            Type::Struct(_) => "tuple".to_owned(),
+            _ => unreachable!(),
+        }
+    }
+
+    /// Give the type of an memory array after dereference.
+    pub fn array_deref(&self) -> Self {
+        match self {
+            Type::String | Type::DynamicBytes => Type::Ref(Box::new(Type::Uint(8))),
+            Type::Ref(t) => t.array_deref(),
+            Type::Array(ty, dim) if dim.len() > 1 => {
+                Type::Array(ty.clone(), dim[..dim.len() - 1].to_vec())
+            }
+            Type::Array(ty, dim) if dim.len() == 1 => Type::Ref(Box::new(*ty.clone())),
+            Type::Bytes(_) => Type::Bytes(1),
+            _ => panic!("deref on non-array"),
+        }
+    }
+
+    /// Given an array, return the type of its elements
+    pub fn array_elem(&self) -> Self {
+        match self {
+            Type::Array(ty, dim) if dim.len() > 1 => {
+                Type::Array(ty.clone(), dim[..dim.len() - 1].to_vec())
+            }
+            Type::Array(ty, dim) if dim.len() == 1 => *ty.clone(),
+            _ => panic!("not an array"),
+        }
+    }
+
+    /// Give the type of an storage array after dereference. This can only be used on
+    /// array types and will cause a panic otherwise.
+    pub fn storage_array_elem(&self) -> Self {
+        match self {
+            Type::Array(ty, dim) if dim.len() > 1 => Type::StorageRef(Box::new(Type::Array(
+                ty.clone(),
+                dim[..dim.len() - 1].to_vec(),
+            ))),
+            Type::Array(ty, dim) if dim.len() == 1 => Type::StorageRef(Box::new(*ty.clone())),
+            Type::StorageRef(ty) => ty.storage_array_elem(),
+            _ => panic!("deref on non-array"),
+        }
+    }
+
+    /// Give the length of the outer array. This can only be called on array types
+    /// and will panic otherwise.
+    pub fn array_length(&self) -> Option<&BigInt> {
+        match self {
+            Type::StorageRef(ty) => ty.array_length(),
+            Type::Ref(ty) => ty.array_length(),
+            Type::Array(_, dim) => dim.last().unwrap().as_ref(),
+            _ => panic!("array_length on non-array"),
+        }
+    }
+
+    /// Calculate how much memory we expect this type to use when allocated on the
+    /// stack or on the heap. Depending on the llvm implementation there might be
+    /// padding between elements which is not accounted for.
+    pub fn size_hint(&self, ns: &Namespace) -> BigInt {
+        match self {
+            Type::Enum(_) => BigInt::one(),
+            Type::Bool => BigInt::one(),
+            Type::Contract(_) | Type::Address(_) => BigInt::from(ns.address_length),
+            Type::Bytes(n) => BigInt::from(*n),
+            Type::Uint(n) | Type::Int(n) => BigInt::from(n / 8),
+            Type::Array(ty, dims) => {
+                let pointer_size = BigInt::from(4);
+                ty.size_hint(ns).mul(
+                    dims.iter()
+                        .map(|d| match d {
+                            None => &pointer_size,
+                            Some(d) => d,
+                        })
+                        .product::<BigInt>(),
+                )
+            }
+            Type::Struct(n) => ns.structs[*n]
+                .fields
+                .iter()
+                .map(|f| f.ty.size_hint(ns))
+                .sum(),
+            Type::String | Type::DynamicBytes => BigInt::from(4),
+            _ => unimplemented!(),
+        }
+    }
+
+    pub fn bits(&self, ns: &Namespace) -> u16 {
+        match self {
+            Type::Address(_) => ns.address_length as u16 * 8,
+            Type::Bool => 1,
+            Type::Int(n) => *n,
+            Type::Uint(n) => *n,
+            Type::Bytes(n) => *n as u16 * 8,
+            Type::Enum(n) => ns.enums[*n].ty.bits(ns),
+            _ => panic!("type not allowed"),
+        }
+    }
+
+    pub fn is_signed_int(&self) -> bool {
+        match self {
+            Type::Int(_) => true,
+            Type::Ref(r) => r.is_signed_int(),
+            Type::StorageRef(r) => r.is_signed_int(),
+            _ => false,
+        }
+    }
+
+    pub fn ordered(&self) -> bool {
+        match self {
+            Type::Int(_) => true,
+            Type::Uint(_) => true,
+            Type::Struct(_) => unreachable!(),
+            Type::Array(_, _) => unreachable!(),
+            Type::Ref(r) => r.ordered(),
+            Type::StorageRef(r) => r.ordered(),
+            _ => false,
+        }
+    }
+
+    /// Calculate how many storage slots a type occupies. Note that storage arrays can
+    /// be very large
+    pub fn storage_slots(&self, ns: &Namespace) -> BigInt {
+        match self {
+            Type::StorageRef(r) | Type::Ref(r) => r.storage_slots(ns),
+            Type::Struct(n) => ns.structs[*n]
+                .fields
+                .iter()
+                .map(|f| f.ty.storage_slots(ns))
+                .sum(),
+            Type::Array(ty, dims) => {
+                let one = BigInt::one();
+
+                ty.storage_slots(ns)
+                    * dims
+                        .iter()
+                        .map(|l| match l {
+                            None => &one,
+                            Some(l) => l,
+                        })
+                        .product::<BigInt>()
+            }
+            _ => BigInt::one(),
+        }
+    }
+
+    /// Is this type an reference type in the solidity language? (struct, array, mapping)
+    pub fn is_reference_type(&self) -> bool {
+        match self {
+            Type::Bool => false,
+            Type::Address(_) => false,
+            Type::Int(_) => false,
+            Type::Uint(_) => false,
+            Type::Bytes(_) => false,
+            Type::Enum(_) => false,
+            Type::Struct(_) => true,
+            Type::Array(_, _) => true,
+            Type::DynamicBytes => true,
+            Type::String => true,
+            Type::Mapping(_, _) => true,
+            Type::Contract(_) => false,
+            Type::Ref(r) => r.is_reference_type(),
+            Type::StorageRef(r) => r.is_reference_type(),
+            _ => unreachable!(),
+        }
+    }
+
+    /// Does this type contain any types which are variable-length
+    pub fn is_dynamic(&self, ns: &Namespace) -> bool {
+        match self {
+            Type::String | Type::DynamicBytes => true,
+            Type::Ref(r) => r.is_dynamic(ns),
+            Type::Array(ty, dim) => {
+                if dim.iter().any(|d| d.is_none()) {
+                    return true;
+                }
+
+                ty.is_dynamic(ns)
+            }
+            Type::Struct(n) => ns.structs[*n].fields.iter().any(|f| f.ty.is_dynamic(ns)),
+            Type::StorageRef(r) => r.is_dynamic(ns),
+            _ => false,
+        }
+    }
+
+    /// Can this type have a calldata, memory, or storage location. This is to be
+    /// compatible with ethereum solidity. Opinions on whether other types should be
+    /// allowed be storage are welcome.
+    pub fn can_have_data_location(&self) -> bool {
+        match self {
+            Type::Array(_, _)
+            | Type::Struct(_)
+            | Type::Mapping(_, _)
+            | Type::String
+            | Type::DynamicBytes => true,
+            _ => false,
+        }
+    }
+
+    /// Is this a reference to contract storage?
+    pub fn is_contract_storage(&self) -> bool {
+        match self {
+            Type::StorageRef(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Is this a storage bytes string
+    pub fn is_storage_bytes(&self) -> bool {
+        if let Type::StorageRef(ty) = self {
+            if let Type::DynamicBytes = ty.as_ref() {
+                return true;
+            }
+        }
+
+        false
+    }
+
+    /// Is this a mapping
+    pub fn is_mapping(&self) -> bool {
+        match self {
+            Type::Mapping(_, _) => true,
+            Type::StorageRef(ty) => ty.is_mapping(),
+            _ => false,
+        }
+    }
+
+    /// Does the type contain any mapping type
+    pub fn contains_mapping(&self, ns: &Namespace) -> bool {
+        match self {
+            Type::Mapping(_, _) => true,
+            Type::Array(ty, _) => ty.contains_mapping(ns),
+            Type::Struct(n) => ns.structs[*n]
+                .fields
+                .iter()
+                .any(|f| f.ty.contains_mapping(ns)),
+            Type::StorageRef(r) | Type::Ref(r) => r.contains_mapping(ns),
+            _ => false,
+        }
+    }
+
+    /// If the type is Ref or StorageRef, get the underlying type
+    pub fn deref_any(&self) -> &Self {
+        match self {
+            Type::StorageRef(r) => r,
+            Type::Ref(r) => r,
+            _ => self,
+        }
+    }
+
+    /// If the type is Ref, get the underlying type
+    pub fn deref(&self) -> &Self {
+        match self {
+            Type::Ref(r) => r,
+            _ => self,
+        }
+    }
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub struct StructField {
+    pub name: String,
+    pub loc: pt::Loc,
+    pub ty: Type,
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub struct StructDecl {
+    pub name: String,
+    pub loc: pt::Loc,
+    pub contract: Option<String>,
+    pub fields: Vec<StructField>,
+}
+
+impl StructDecl {
+    /// Make the struct name into a string for printing. The enum can be declared either
+    /// inside or outside a contract.
+    pub fn print_to_string(&self) -> String {
+        match &self.contract {
+            Some(c) => format!("{}.{}", c, self.name),
+            None => self.name.to_owned(),
+        }
+    }
+}
+
+pub struct EnumDecl {
+    pub name: String,
+    pub contract: Option<String>,
+    pub ty: Type,
+    pub values: HashMap<String, (pt::Loc, usize)>,
+}
+
+impl EnumDecl {
+    /// Make the enum name into a string for printing. The enum can be declared either
+    /// inside or outside a contract.
+    pub fn print_to_string(&self) -> String {
+        match &self.contract {
+            Some(c) => format!("{}.{}", c, self.name),
+            None => self.name.to_owned(),
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct Parameter {
+    pub loc: pt::Loc,
+    pub name: String,
+    pub ty: Type,
+}
+
+pub struct Function {
+    pub doc: Vec<String>,
+    pub loc: pt::Loc,
+    pub name: String,
+    pub ty: pt::FunctionTy,
+    pub signature: String,
+    pub ast_index: Option<usize>,
+    pub mutability: Option<pt::StateMutability>,
+    pub visibility: pt::Visibility,
+    pub params: Vec<Parameter>,
+    pub returns: Vec<Parameter>,
+    pub noreturn: bool,
+    pub check_nonpayable: bool,
+    pub body: Vec<Statement>,
+    pub symtable: Symtable,
+    pub cfg: Option<ControlFlowGraph>,
+}
+
+impl Function {
+    pub fn new(
+        loc: pt::Loc,
+        name: String,
+        doc: Vec<String>,
+        ty: pt::FunctionTy,
+        ast_index: Option<usize>,
+        mutability: Option<pt::StateMutability>,
+        visibility: pt::Visibility,
+        params: Vec<Parameter>,
+        returns: Vec<Parameter>,
+        ns: &Namespace,
+    ) -> Self {
+        let signature = format!(
+            "{}({})",
+            name,
+            params
+                .iter()
+                .map(|p| p.ty.to_signature_string(ns))
+                .collect::<Vec<String>>()
+                .join(",")
+        );
+
+        Function {
+            doc,
+            loc,
+            name,
+            ty,
+            signature,
+            ast_index,
+            mutability,
+            visibility,
+            params,
+            returns,
+            noreturn: false,
+            check_nonpayable: false,
+            body: Vec::new(),
+            cfg: None,
+            symtable: Symtable::new(),
+        }
+    }
+
+    /// Generate selector for this function
+    pub fn selector(&self) -> u32 {
+        let res = keccak256(self.signature.as_bytes());
+
+        u32::from_le_bytes([res[0], res[1], res[2], res[3]])
+    }
+
+    /// Is this a constructor
+    pub fn is_constructor(&self) -> bool {
+        self.ty == pt::FunctionTy::Constructor
+    }
+
+    /// Does this function have the payable state
+    pub fn is_payable(&self) -> bool {
+        if let Some(pt::StateMutability::Payable(_)) = self.mutability {
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Is this function accessable externally
+    pub fn is_public(&self) -> bool {
+        match self.visibility {
+            pt::Visibility::Public(_) | pt::Visibility::External(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Return a unique string for this function which is a valid wasm symbol
+    pub fn wasm_symbol(&self, ns: &Namespace) -> String {
+        let mut sig = self.name.to_owned();
+
+        if !self.params.is_empty() {
+            sig.push_str("__");
+
+            for (i, p) in self.params.iter().enumerate() {
+                if i > 0 {
+                    sig.push('_');
+                }
+
+                fn type_to_wasm_name(ty: &Type, ns: &Namespace) -> String {
+                    match ty {
+                        Type::Bool => "bool".to_string(),
+                        Type::Address(_) => "address".to_string(),
+                        Type::Int(n) => format!("int{}", n),
+                        Type::Uint(n) => format!("uint{}", n),
+                        Type::Bytes(n) => format!("bytes{}", n),
+                        Type::DynamicBytes => "bytes".to_string(),
+                        Type::String => "string".to_string(),
+                        Type::Enum(i) => ns.enums[*i].print_to_string(),
+                        Type::Struct(i) => ns.structs[*i].print_to_string(),
+                        Type::Array(ty, len) => format!(
+                            "{}{}",
+                            type_to_wasm_name(ty, ns),
+                            len.iter()
+                                .map(|r| match r {
+                                    None => ":".to_string(),
+                                    Some(r) => format!(":{}", r),
+                                })
+                                .collect::<String>()
+                        ),
+                        Type::Mapping(k, v) => format!(
+                            "mapping:{}:{}",
+                            type_to_wasm_name(k, ns),
+                            type_to_wasm_name(v, ns)
+                        ),
+                        Type::Contract(i) => ns.contracts[*i].name.to_owned(),
+                        Type::Ref(r) => type_to_wasm_name(r, ns),
+                        Type::StorageRef(r) => type_to_wasm_name(r, ns),
+                        _ => unreachable!(),
+                    }
+                }
+
+                sig.push_str(&type_to_wasm_name(&p.ty, ns));
+            }
+        }
+
+        sig
+    }
+}
+
+impl From<&pt::Type> for Type {
+    fn from(p: &pt::Type) -> Type {
+        match p {
+            pt::Type::Bool => Type::Bool,
+            pt::Type::Address => Type::Address(false),
+            pt::Type::AddressPayable => Type::Address(true),
+            pt::Type::Payable => Type::Address(true),
+            pt::Type::Int(n) => Type::Int(*n),
+            pt::Type::Uint(n) => Type::Uint(*n),
+            pt::Type::Bytes(n) => Type::Bytes(*n),
+            pt::Type::String => Type::String,
+            pt::Type::DynamicBytes => Type::DynamicBytes,
+            // needs special casing
+            pt::Type::Mapping(_, _, _) => unimplemented!(),
+        }
+    }
+}
+
+pub enum ContractVariableType {
+    Storage(BigInt),
+    Constant,
+}
+
+pub struct ContractVariable {
+    pub doc: Vec<String>,
+    pub name: String,
+    pub ty: Type,
+    pub visibility: pt::Visibility,
+    pub var: ContractVariableType,
+    pub initializer: Option<Expression>,
+}
+
+impl ContractVariable {
+    pub fn is_storage(&self) -> bool {
+        if let ContractVariableType::Storage(_) = self.var {
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn get_storage_slot(&self) -> Expression {
+        if let ContractVariableType::Storage(n) = &self.var {
+            Expression::NumberLiteral(pt::Loc(0, 0), Type::Uint(256), n.clone())
+        } else {
+            panic!("get_storage_slot called on non-storage variable");
+        }
+    }
+}
+
+pub enum Symbol {
+    Enum(pt::Loc, usize),
+    Function(Vec<(pt::Loc, usize)>),
+    Variable(pt::Loc, usize),
+    Struct(pt::Loc, usize),
+    Contract(pt::Loc, usize),
+}
+
+/// When resolving a Solidity file, this holds all the resolved items
+pub struct Namespace {
+    pub target: Target,
+    pub enums: Vec<EnumDecl>,
+    pub structs: Vec<StructDecl>,
+    pub contracts: Vec<Contract>,
+    pub address_length: usize,
+    pub value_length: usize,
+    pub diagnostics: Vec<output::Output>,
+    pub symbols: HashMap<(Option<usize>, String), Symbol>,
+}
+
+pub struct Contract {
+    pub doc: Vec<String>,
+    pub name: String,
+    // events
+    pub functions: Vec<Function>,
+    pub variables: Vec<ContractVariable>,
+    pub top_of_contract_storage: BigInt,
+    pub creates: Vec<usize>,
+    pub initializer: ControlFlowGraph,
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub enum Expression {
+    FunctionArg(pt::Loc, Type, usize),
+    BoolLiteral(pt::Loc, bool),
+    BytesLiteral(pt::Loc, Type, Vec<u8>),
+    CodeLiteral(pt::Loc, usize, bool),
+    NumberLiteral(pt::Loc, Type, BigInt),
+    StructLiteral(pt::Loc, Type, Vec<Expression>),
+    ArrayLiteral(pt::Loc, Type, Vec<u32>, Vec<Expression>),
+    ConstArrayLiteral(pt::Loc, Type, Vec<u32>, Vec<Expression>),
+    Add(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    Subtract(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    Multiply(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    UDivide(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    SDivide(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    UModulo(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    SModulo(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    Power(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    BitwiseOr(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    BitwiseAnd(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    BitwiseXor(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    ShiftLeft(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    ShiftRight(pt::Loc, Type, Box<Expression>, Box<Expression>, bool),
+    Variable(pt::Loc, Type, usize),
+    ConstantVariable(pt::Loc, Type, usize),
+    StorageVariable(pt::Loc, Type, usize),
+    Load(pt::Loc, Type, Box<Expression>),
+    StorageLoad(pt::Loc, Type, Box<Expression>),
+    ZeroExt(pt::Loc, Type, Box<Expression>),
+    SignExt(pt::Loc, Type, Box<Expression>),
+    Trunc(pt::Loc, Type, Box<Expression>),
+    Cast(pt::Loc, Type, Box<Expression>),
+
+    PreIncrement(pt::Loc, Type, Box<Expression>),
+    PreDecrement(pt::Loc, Type, Box<Expression>),
+    PostIncrement(pt::Loc, Type, Box<Expression>),
+    PostDecrement(pt::Loc, Type, Box<Expression>),
+    Assign(pt::Loc, Type, Box<Expression>, Box<Expression>),
+
+    UMore(pt::Loc, Box<Expression>, Box<Expression>),
+    ULess(pt::Loc, Box<Expression>, Box<Expression>),
+    UMoreEqual(pt::Loc, Box<Expression>, Box<Expression>),
+    ULessEqual(pt::Loc, Box<Expression>, Box<Expression>),
+    SMore(pt::Loc, Box<Expression>, Box<Expression>),
+    SLess(pt::Loc, Box<Expression>, Box<Expression>),
+    SMoreEqual(pt::Loc, Box<Expression>, Box<Expression>),
+    SLessEqual(pt::Loc, Box<Expression>, Box<Expression>),
+    Equal(pt::Loc, Box<Expression>, Box<Expression>),
+    NotEqual(pt::Loc, Box<Expression>, Box<Expression>),
+
+    Not(pt::Loc, Box<Expression>),
+    Complement(pt::Loc, Type, Box<Expression>),
+    UnaryMinus(pt::Loc, Type, Box<Expression>),
+
+    Ternary(
+        pt::Loc,
+        Type,
+        Box<Expression>,
+        Box<Expression>,
+        Box<Expression>,
+    ),
+    ArraySubscript(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    StructMember(pt::Loc, Type, Box<Expression>, usize),
+
+    AllocDynamicArray(pt::Loc, Type, Box<Expression>, Option<Vec<u8>>),
+    DynamicArrayLength(pt::Loc, Box<Expression>),
+    DynamicArraySubscript(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    StorageBytesSubscript(pt::Loc, Box<Expression>, Box<Expression>),
+    StorageBytesPush(pt::Loc, Box<Expression>, Box<Expression>),
+    StorageBytesPop(pt::Loc, Box<Expression>),
+    StorageBytesLength(pt::Loc, Box<Expression>),
+    StringCompare(pt::Loc, StringLocation, StringLocation),
+    StringConcat(pt::Loc, Type, StringLocation, StringLocation),
+
+    Or(pt::Loc, Box<Expression>, Box<Expression>),
+    And(pt::Loc, Box<Expression>, Box<Expression>),
+    InternalFunctionCall(pt::Loc, Vec<Type>, usize, Vec<Expression>),
+    ExternalFunctionCall {
+        loc: pt::Loc,
+        returns: Vec<Type>,
+        contract_no: usize,
+        function_no: usize,
+        address: Box<Expression>,
+        args: Vec<Expression>,
+        value: Box<Expression>,
+        gas: Box<Expression>,
+    },
+    Constructor {
+        loc: pt::Loc,
+        contract_no: usize,
+        constructor_no: usize,
+        args: Vec<Expression>,
+        gas: Box<Expression>,
+        value: Option<Box<Expression>>,
+        salt: Option<Box<Expression>>,
+    },
+    Keccak256(pt::Loc, Type, Vec<Expression>),
+
+    ReturnData(pt::Loc),
+    GetAddress(pt::Loc, Type),
+    Balance(pt::Loc, Type, Box<Expression>),
+    Builtin(pt::Loc, Vec<Type>, Builtin, Vec<Expression>),
+    List(pt::Loc, Vec<Expression>),
+    Poison,
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub enum StringLocation {
+    CompileTime(Vec<u8>),
+    RunTime(Box<Expression>),
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub enum Builtin {
+    PayableSend,
+    PayableTransfer,
+    ArrayPush,
+    ArrayPop,
+    BytesPush,
+    BytesPop,
+    Assert,
+    Print,
+    Revert,
+    Require,
+    SelfDestruct,
+    Keccak256,
+    Ripemd160,
+    Sha256,
+    Blake2_128,
+    Blake2_256,
+}
+
+#[derive(Clone, Debug)]
+#[allow(clippy::large_enum_variant)]
+pub enum Statement {
+    VariableDecl(pt::Loc, usize, Parameter, Option<Expression>),
+    If(pt::Loc, bool, Expression, Vec<Statement>, Vec<Statement>),
+    While(pt::Loc, bool, Expression, Vec<Statement>),
+    For {
+        loc: pt::Loc,
+        reachable: bool,
+        init: Vec<Statement>,
+        cond: Option<Expression>,
+        next: Vec<Statement>,
+        body: Vec<Statement>,
+    },
+    DoWhile(pt::Loc, bool, Vec<Statement>, Expression),
+    Expression(pt::Loc, bool, Expression),
+    Delete(pt::Loc, Type, Expression),
+    Destructure(pt::Loc, Vec<DestructureField>, Expression),
+    Continue(pt::Loc),
+    Break(pt::Loc),
+    Return(pt::Loc, Vec<Expression>),
+    TryCatch {
+        loc: pt::Loc,
+        reachable: bool,
+        expr: Expression,
+        returns: Vec<(Option<usize>, Parameter)>,
+        ok_stmt: Vec<Statement>,
+        error: Option<(Option<usize>, Parameter, Vec<Statement>)>,
+        catch_param: Parameter,
+        catch_param_pos: Option<usize>,
+        catch_stmt: Vec<Statement>,
+    },
+}
+
+#[derive(Clone, Debug)]
+pub enum DestructureField {
+    None,
+    Expression(Expression),
+    VariableDecl(usize, Parameter),
+}
+
+impl Statement {
+    pub fn reachable(&self) -> bool {
+        match self {
+            Statement::Destructure(_, _, _) | Statement::VariableDecl(_, _, _, _) => true,
+            Statement::If(_, reachable, _, _, _)
+            | Statement::While(_, reachable, _, _)
+            | Statement::DoWhile(_, reachable, _, _)
+            | Statement::Expression(_, reachable, _) => *reachable,
+            Statement::Delete(_, _, _) => true,
+            Statement::Continue(_) | Statement::Break(_) | Statement::Return(_, _) => false,
+            Statement::For { reachable, .. } | Statement::TryCatch { reachable, .. } => *reachable,
+        }
+    }
+}

+ 183 - 0
src/sema/builtin.rs

@@ -0,0 +1,183 @@
+use crate::Target;
+use output::Output;
+use parser::pt;
+use sema::ast::{Builtin, Expression, Namespace, Type};
+use sema::expression::cast;
+
+struct Prototype {
+    pub builtin: Builtin,
+    pub this: Option<Type>,
+    pub name: &'static str,
+    pub args: &'static [Type],
+    pub ret: &'static [Type],
+    pub target: Option<Target>,
+}
+
+// A list of all Solidity builtins
+static PROTO_TYPES: [Prototype; 12] = [
+    Prototype {
+        builtin: Builtin::Assert,
+        name: "assert",
+        this: None,
+        args: &[Type::Bool],
+        ret: &[Type::Void],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Print,
+        name: "print",
+        this: None,
+        args: &[Type::String],
+        ret: &[Type::Void],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Require,
+        name: "require",
+        this: None,
+        args: &[Type::Bool],
+        ret: &[Type::Void],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Require,
+        name: "require",
+        this: None,
+        args: &[Type::Bool, Type::String],
+        ret: &[Type::Void],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Revert,
+        name: "revert",
+        this: None,
+        args: &[],
+        ret: &[Type::Unreachable],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Revert,
+        name: "revert",
+        this: None,
+        args: &[Type::String],
+        ret: &[Type::Unreachable],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::SelfDestruct,
+        name: "selfdestruct",
+        this: None,
+        args: &[Type::Address(true)],
+        ret: &[Type::Unreachable],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Keccak256,
+        name: "keccak256",
+        this: None,
+        args: &[Type::DynamicBytes],
+        ret: &[Type::Bytes(32)],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Ripemd160,
+        name: "ripemd160",
+        this: None,
+        args: &[Type::DynamicBytes],
+        ret: &[Type::Bytes(20)],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Sha256,
+        name: "sha256",
+        this: None,
+        args: &[Type::DynamicBytes],
+        ret: &[Type::Bytes(32)],
+        target: None,
+    },
+    Prototype {
+        builtin: Builtin::Blake2_128,
+        name: "blake2_128",
+        this: None,
+        args: &[Type::DynamicBytes],
+        ret: &[Type::Bytes(16)],
+        target: Some(Target::Substrate),
+    },
+    Prototype {
+        builtin: Builtin::Blake2_256,
+        name: "blake2_256",
+        this: None,
+        args: &[Type::DynamicBytes],
+        ret: &[Type::Bytes(32)],
+        target: Some(Target::Substrate),
+    },
+];
+
+/// Does function call match builtin
+pub fn is_builtin_call(fname: &str) -> bool {
+    PROTO_TYPES
+        .iter()
+        .any(|p| p.name == fname && p.this.is_none())
+}
+
+/// Resolve a builtin call
+pub fn resolve(
+    loc: &pt::Loc,
+    id: &pt::Identifier,
+    args: Vec<Expression>,
+    ns: &mut Namespace,
+) -> Result<Expression, ()> {
+    let matches = PROTO_TYPES
+        .iter()
+        .filter(|p| p.name == id.name && p.this.is_none())
+        .collect::<Vec<&Prototype>>();
+    let marker = ns.diagnostics.len();
+    for func in &matches {
+        if func.args.len() != args.len() {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "builtin function ‘{}’ expects {} arguments, {} provided",
+                    func.name,
+                    func.args.len(),
+                    args.len()
+                ),
+            ));
+            continue;
+        }
+
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        // check if arguments can be implicitly casted
+        for (i, arg) in args.iter().enumerate() {
+            match cast(&pt::Loc(0, 0), arg.clone(), &func.args[i], true, ns) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+
+        if matches {
+            ns.diagnostics.truncate(marker);
+            return Ok(Expression::Builtin(
+                *loc,
+                func.ret.to_vec(),
+                func.builtin.clone(),
+                cast_args,
+            ));
+        }
+    }
+
+    if matches.len() != 1 {
+        ns.diagnostics.truncate(marker);
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "cannot find overloaded function which matches signature".to_string(),
+        ));
+    }
+
+    Err(())
+}

+ 128 - 0
src/sema/eval.rs

@@ -0,0 +1,128 @@
+use num_bigint::BigInt;
+use num_bigint::Sign;
+use num_traits::One;
+use num_traits::ToPrimitive;
+use num_traits::Zero;
+
+use super::ast::{Expression, Namespace};
+use output::Output;
+use parser::pt::Loc;
+
+/// Resolve an expression where a compile-time constant is expected
+pub fn eval_const_number(
+    expr: &Expression,
+    contract_no: Option<usize>,
+    ns: &Namespace,
+) -> Result<(Loc, BigInt), Output> {
+    match expr {
+        Expression::Add(loc, _, l, r) => Ok((
+            *loc,
+            eval_const_number(l, contract_no, ns)?.1 + eval_const_number(r, contract_no, ns)?.1,
+        )),
+        Expression::Subtract(loc, _, l, r) => Ok((
+            *loc,
+            eval_const_number(l, contract_no, ns)?.1 - eval_const_number(r, contract_no, ns)?.1,
+        )),
+        Expression::Multiply(loc, _, l, r) => Ok((
+            *loc,
+            eval_const_number(l, contract_no, ns)?.1 * eval_const_number(r, contract_no, ns)?.1,
+        )),
+        Expression::UDivide(loc, _, l, r) | Expression::SDivide(loc, _, l, r) => {
+            let divisor = eval_const_number(r, contract_no, ns)?.1;
+
+            if divisor.is_zero() {
+                Err(Output::error(*loc, "divide by zero".to_string()))
+            } else {
+                Ok((*loc, eval_const_number(l, contract_no, ns)?.1 / divisor))
+            }
+        }
+        Expression::UModulo(loc, _, l, r) | Expression::SModulo(loc, _, l, r) => {
+            let divisor = eval_const_number(r, contract_no, ns)?.1;
+
+            if divisor.is_zero() {
+                Err(Output::error(*loc, "divide by zero".to_string()))
+            } else {
+                Ok((*loc, eval_const_number(l, contract_no, ns)?.1 % divisor))
+            }
+        }
+        Expression::BitwiseAnd(loc, _, l, r) => Ok((
+            *loc,
+            eval_const_number(l, contract_no, ns)?.1 & eval_const_number(r, contract_no, ns)?.1,
+        )),
+        Expression::BitwiseOr(loc, _, l, r) => Ok((
+            *loc,
+            eval_const_number(l, contract_no, ns)?.1 | eval_const_number(r, contract_no, ns)?.1,
+        )),
+        Expression::BitwiseXor(loc, _, l, r) => Ok((
+            *loc,
+            eval_const_number(l, contract_no, ns)?.1 ^ eval_const_number(r, contract_no, ns)?.1,
+        )),
+        Expression::Power(loc, _, base, exp) => {
+            let b = eval_const_number(base, contract_no, ns)?.1;
+            let mut e = eval_const_number(exp, contract_no, ns)?.1;
+
+            if e.sign() == Sign::Minus {
+                Err(Output::error(
+                    expr.loc(),
+                    "power cannot take negative number as exponent".to_string(),
+                ))
+            } else if e.sign() == Sign::NoSign {
+                Ok((*loc, BigInt::one()))
+            } else {
+                let mut res = b.clone();
+                e -= BigInt::one();
+                while e.sign() == Sign::Plus {
+                    res *= b.clone();
+                    e -= BigInt::one();
+                }
+                Ok((*loc, res))
+            }
+        }
+        Expression::ShiftLeft(loc, _, left, right) => {
+            let l = eval_const_number(left, contract_no, ns)?.1;
+            let r = eval_const_number(right, contract_no, ns)?.1;
+            let r = match r.to_usize() {
+                Some(r) => r,
+                None => {
+                    return Err(Output::error(
+                        expr.loc(),
+                        format!("cannot left shift by {}", r),
+                    ));
+                }
+            };
+            Ok((*loc, l << r))
+        }
+        Expression::ShiftRight(loc, _, left, right, _) => {
+            let l = eval_const_number(left, contract_no, ns)?.1;
+            let r = eval_const_number(right, contract_no, ns)?.1;
+            let r = match r.to_usize() {
+                Some(r) => r,
+                None => {
+                    return Err(Output::error(
+                        expr.loc(),
+                        format!("cannot right shift by {}", r),
+                    ));
+                }
+            };
+            Ok((*loc, l >> r))
+        }
+        Expression::NumberLiteral(loc, _, n) => Ok((*loc, n.clone())),
+        Expression::ZeroExt(loc, _, n) => Ok((*loc, eval_const_number(n, contract_no, ns)?.1)),
+        Expression::Not(loc, n) => Ok((*loc, !eval_const_number(n, contract_no, ns)?.1)),
+        Expression::Complement(loc, _, n) => Ok((*loc, !eval_const_number(n, contract_no, ns)?.1)),
+        Expression::UnaryMinus(loc, _, n) => Ok((*loc, -eval_const_number(n, contract_no, ns)?.1)),
+        Expression::ConstantVariable(_, _, no) => {
+            let expr = ns.contracts[contract_no.unwrap()].variables[*no]
+                .initializer
+                .as_ref()
+                .unwrap()
+                .clone();
+
+            eval_const_number(&expr, contract_no, ns)
+        }
+        _ => Err(Output::error(
+            expr.loc(),
+            "expression not allowed in constant number expression".to_string(),
+        )),
+    }
+}

+ 4268 - 0
src/sema/expression.rs

@@ -0,0 +1,4268 @@
+use num_bigint::BigInt;
+use num_bigint::Sign;
+use num_traits::FromPrimitive;
+use num_traits::Num;
+use num_traits::One;
+use num_traits::Pow;
+use num_traits::ToPrimitive;
+use num_traits::Zero;
+use std::cmp;
+use std::cmp::Ordering;
+use std::collections::HashMap;
+use std::ops::Shl;
+use std::ops::Sub;
+
+use hex;
+use output::Output;
+use parser::pt;
+use parser::pt::Loc;
+use sema::address::to_hexstr_eip55;
+use sema::ast::{
+    Builtin, ContractVariableType, Expression, Function, Namespace, StringLocation, Type,
+};
+use sema::builtin;
+use sema::eval::eval_const_number;
+use sema::symtable::Symtable;
+
+impl Expression {
+    /// Return the location for this expression
+    pub fn loc(&self) -> Loc {
+        match self {
+            Expression::FunctionArg(loc, _, _)
+            | Expression::BoolLiteral(loc, _)
+            | Expression::BytesLiteral(loc, _, _)
+            | Expression::CodeLiteral(loc, _, _)
+            | Expression::NumberLiteral(loc, _, _)
+            | Expression::StructLiteral(loc, _, _)
+            | Expression::ArrayLiteral(loc, _, _, _)
+            | Expression::ConstArrayLiteral(loc, _, _, _)
+            | Expression::Add(loc, _, _, _)
+            | Expression::Subtract(loc, _, _, _)
+            | Expression::Multiply(loc, _, _, _)
+            | Expression::UDivide(loc, _, _, _)
+            | Expression::SDivide(loc, _, _, _)
+            | Expression::UModulo(loc, _, _, _)
+            | Expression::SModulo(loc, _, _, _)
+            | Expression::Power(loc, _, _, _)
+            | Expression::BitwiseOr(loc, _, _, _)
+            | Expression::BitwiseAnd(loc, _, _, _)
+            | Expression::BitwiseXor(loc, _, _, _)
+            | Expression::ShiftLeft(loc, _, _, _)
+            | Expression::ShiftRight(loc, _, _, _, _)
+            | Expression::Variable(loc, _, _)
+            | Expression::ConstantVariable(loc, _, _)
+            | Expression::StorageVariable(loc, _, _)
+            | Expression::Load(loc, _, _)
+            | Expression::StorageLoad(loc, _, _)
+            | Expression::ZeroExt(loc, _, _)
+            | Expression::SignExt(loc, _, _)
+            | Expression::Trunc(loc, _, _)
+            | Expression::Cast(loc, _, _)
+            | Expression::UMore(loc, _, _)
+            | Expression::ULess(loc, _, _)
+            | Expression::UMoreEqual(loc, _, _)
+            | Expression::ULessEqual(loc, _, _)
+            | Expression::SMore(loc, _, _)
+            | Expression::SLess(loc, _, _)
+            | Expression::SMoreEqual(loc, _, _)
+            | Expression::SLessEqual(loc, _, _)
+            | Expression::Equal(loc, _, _)
+            | Expression::NotEqual(loc, _, _)
+            | Expression::Not(loc, _)
+            | Expression::Complement(loc, _, _)
+            | Expression::UnaryMinus(loc, _, _)
+            | Expression::Ternary(loc, _, _, _, _)
+            | Expression::ArraySubscript(loc, _, _, _)
+            | Expression::StructMember(loc, _, _, _)
+            | Expression::Or(loc, _, _)
+            | Expression::AllocDynamicArray(loc, _, _, _)
+            | Expression::DynamicArrayLength(loc, _)
+            | Expression::DynamicArraySubscript(loc, _, _, _)
+            | Expression::StorageBytesSubscript(loc, _, _)
+            | Expression::StorageBytesPush(loc, _, _)
+            | Expression::StorageBytesPop(loc, _)
+            | Expression::StorageBytesLength(loc, _)
+            | Expression::StringCompare(loc, _, _)
+            | Expression::StringConcat(loc, _, _, _)
+            | Expression::Keccak256(loc, _, _)
+            | Expression::ReturnData(loc)
+            | Expression::InternalFunctionCall(loc, _, _, _)
+            | Expression::ExternalFunctionCall { loc, .. }
+            | Expression::Constructor { loc, .. }
+            | Expression::GetAddress(loc, _)
+            | Expression::Balance(loc, _, _)
+            | Expression::PreIncrement(loc, _, _)
+            | Expression::PreDecrement(loc, _, _)
+            | Expression::PostIncrement(loc, _, _)
+            | Expression::PostDecrement(loc, _, _)
+            | Expression::Builtin(loc, _, _, _)
+            | Expression::Assign(loc, _, _, _)
+            | Expression::List(loc, _)
+            | Expression::And(loc, _, _) => *loc,
+            Expression::Poison => unreachable!(),
+        }
+    }
+
+    /// Return the type for this expression. This assumes the expression has a single value,
+    /// panics will occur otherwise
+    pub fn ty(&self) -> Type {
+        match self {
+            Expression::BoolLiteral(_, _)
+            | Expression::UMore(_, _, _)
+            | Expression::ULess(_, _, _)
+            | Expression::UMoreEqual(_, _, _)
+            | Expression::ULessEqual(_, _, _)
+            | Expression::SMore(_, _, _)
+            | Expression::SLess(_, _, _)
+            | Expression::SMoreEqual(_, _, _)
+            | Expression::SLessEqual(_, _, _)
+            | Expression::Equal(_, _, _)
+            | Expression::Or(_, _, _)
+            | Expression::And(_, _, _)
+            | Expression::NotEqual(_, _, _)
+            | Expression::Not(_, _)
+            | Expression::StringCompare(_, _, _) => Type::Bool,
+            Expression::CodeLiteral(_, _, _) => Type::DynamicBytes,
+            Expression::StringConcat(_, ty, _, _)
+            | Expression::FunctionArg(_, ty, _)
+            | Expression::BytesLiteral(_, ty, _)
+            | Expression::NumberLiteral(_, ty, _)
+            | Expression::StructLiteral(_, ty, _)
+            | Expression::ArrayLiteral(_, ty, _, _)
+            | Expression::ConstArrayLiteral(_, ty, _, _)
+            | Expression::Add(_, ty, _, _)
+            | Expression::Subtract(_, ty, _, _)
+            | Expression::Multiply(_, ty, _, _)
+            | Expression::UDivide(_, ty, _, _)
+            | Expression::SDivide(_, ty, _, _)
+            | Expression::UModulo(_, ty, _, _)
+            | Expression::SModulo(_, ty, _, _)
+            | Expression::Power(_, ty, _, _)
+            | Expression::BitwiseOr(_, ty, _, _)
+            | Expression::BitwiseAnd(_, ty, _, _)
+            | Expression::BitwiseXor(_, ty, _, _)
+            | Expression::ShiftLeft(_, ty, _, _)
+            | Expression::ShiftRight(_, ty, _, _, _)
+            | Expression::Variable(_, ty, _)
+            | Expression::ConstantVariable(_, ty, _)
+            | Expression::StorageVariable(_, ty, _)
+            | Expression::Load(_, ty, _)
+            | Expression::StorageLoad(_, ty, _)
+            | Expression::ZeroExt(_, ty, _)
+            | Expression::SignExt(_, ty, _)
+            | Expression::Trunc(_, ty, _)
+            | Expression::Cast(_, ty, _)
+            | Expression::Complement(_, ty, _)
+            | Expression::UnaryMinus(_, ty, _)
+            | Expression::Ternary(_, ty, _, _, _)
+            | Expression::ArraySubscript(_, ty, _, _)
+            | Expression::StructMember(_, ty, _, _)
+            | Expression::AllocDynamicArray(_, ty, _, _)
+            | Expression::DynamicArraySubscript(_, ty, _, _)
+            | Expression::Balance(_, ty, _)
+            | Expression::PreIncrement(_, ty, _)
+            | Expression::PreDecrement(_, ty, _)
+            | Expression::PostIncrement(_, ty, _)
+            | Expression::PostDecrement(_, ty, _)
+            | Expression::GetAddress(_, ty)
+            | Expression::Keccak256(_, ty, _)
+            | Expression::Assign(_, ty, _, _) => ty.clone(),
+            Expression::DynamicArrayLength(_, _) => Type::Uint(32),
+            Expression::StorageBytesLength(_, _) => Type::Uint(32),
+            Expression::StorageBytesSubscript(_, _, _) => {
+                Type::StorageRef(Box::new(Type::Bytes(1)))
+            }
+            Expression::Builtin(_, returns, _, _)
+            | Expression::InternalFunctionCall(_, returns, _, _)
+            | Expression::ExternalFunctionCall { returns, .. } => {
+                assert_eq!(returns.len(), 1);
+                returns[0].clone()
+            }
+            Expression::List(_, list) => {
+                assert_eq!(list.len(), 1);
+
+                list[0].ty()
+            }
+            Expression::Constructor { contract_no, .. } => Type::Contract(*contract_no),
+            Expression::Poison => unreachable!(),
+            // codegen Expressions
+            Expression::ReturnData(_)
+            | Expression::StorageBytesPush(_, _, _)
+            | Expression::StorageBytesPop(_, _) => unreachable!(),
+        }
+    }
+    /// Is this expression 0
+    fn const_zero(&self, contract_no: Option<usize>, ns: &mut Namespace) -> bool {
+        if let Ok((_, value)) = eval_const_number(&self, contract_no, ns) {
+            value == BigInt::zero()
+        } else {
+            false
+        }
+    }
+
+    /// Get the returns for a function call
+    pub fn tys(&self) -> Vec<Type> {
+        match self {
+            Expression::Builtin(_, returns, _, _)
+            | Expression::InternalFunctionCall(_, returns, _, _)
+            | Expression::ExternalFunctionCall { returns, .. } => returns.to_vec(),
+            Expression::List(_, list) => list.iter().map(|e| e.ty()).collect(),
+            _ => unreachable!(),
+        }
+    }
+}
+
+/// Unescape a string literal
+fn unescape(literal: &str, start: usize, ns: &mut Namespace) -> String {
+    let mut s = String::new();
+    let mut indeces = literal.char_indices();
+
+    while let Some((_, ch)) = indeces.next() {
+        if ch != '\\' {
+            s.push(ch);
+            continue;
+        }
+
+        match indeces.next() {
+            Some((_, '\n')) => (),
+            Some((_, '\\')) => s.push('\\'),
+            Some((_, '\'')) => s.push('\''),
+            Some((_, '"')) => s.push('"'),
+            Some((_, 'b')) => s.push('\u{0008}'),
+            Some((_, 'f')) => s.push('\u{000c}'),
+            Some((_, 'n')) => s.push('\n'),
+            Some((_, 'r')) => s.push('\r'),
+            Some((_, 't')) => s.push('\t'),
+            Some((_, 'v')) => s.push('\u{000b}'),
+            Some((i, 'x')) => match get_digits(&mut indeces, 2) {
+                Ok(ch) => match std::char::from_u32(ch) {
+                    Some(ch) => s.push(ch),
+                    None => {
+                        ns.diagnostics.push(Output::error(
+                            pt::Loc(start + i, start + i + 4),
+                            format!("\\x{:02x} is not a valid unicode character", ch),
+                        ));
+                    }
+                },
+                Err(offset) => {
+                    ns.diagnostics.push(Output::error(
+                        pt::Loc(start + i, start + std::cmp::min(literal.len(), offset)),
+                        "\\x escape should be followed by two hex digits".to_string(),
+                    ));
+                }
+            },
+            Some((i, 'u')) => match get_digits(&mut indeces, 4) {
+                Ok(ch) => match std::char::from_u32(ch) {
+                    Some(ch) => s.push(ch),
+                    None => {
+                        ns.diagnostics.push(Output::error(
+                            pt::Loc(start + i, start + i + 6),
+                            format!("\\u{:04x} is not a valid unicode character", ch),
+                        ));
+                    }
+                },
+                Err(offset) => {
+                    ns.diagnostics.push(Output::error(
+                        pt::Loc(start + i, start + std::cmp::min(literal.len(), offset)),
+                        "\\u escape should be followed by four hex digits".to_string(),
+                    ));
+                }
+            },
+            Some((i, ch)) => {
+                ns.diagnostics.push(Output::error(
+                    pt::Loc(start + i, start + i + ch.len_utf8()),
+                    format!("unknown escape character '{}'", ch),
+                ));
+            }
+            None => unreachable!(),
+        }
+    }
+
+    s
+}
+
+/// Get the hex digits for an escaped \x or \u. Returns either the value or
+/// or the offset of the last character
+fn get_digits(input: &mut std::str::CharIndices, len: usize) -> Result<u32, usize> {
+    let mut n = 0;
+    let offset;
+
+    for _ in 0..len {
+        if let Some((_, ch)) = input.next() {
+            if let Some(v) = ch.to_digit(16) {
+                n = (n << 4) + v;
+                continue;
+            }
+            offset = match input.next() {
+                Some((i, _)) => i,
+                None => std::usize::MAX,
+            };
+        } else {
+            offset = std::usize::MAX;
+        }
+
+        return Err(offset);
+    }
+
+    Ok(n)
+}
+
+fn coerce(
+    l: &Type,
+    l_loc: &pt::Loc,
+    r: &Type,
+    r_loc: &pt::Loc,
+    ns: &mut Namespace,
+) -> Result<Type, ()> {
+    let l = match l {
+        Type::Ref(ty) => ty,
+        Type::StorageRef(ty) => ty,
+        _ => l,
+    };
+    let r = match r {
+        Type::Ref(ty) => ty,
+        Type::StorageRef(ty) => ty,
+        _ => r,
+    };
+
+    if *l == *r {
+        return Ok(l.clone());
+    }
+
+    coerce_int(l, l_loc, r, r_loc, true, ns)
+}
+
+fn get_int_length(
+    l: &Type,
+    l_loc: &pt::Loc,
+    allow_bytes: bool,
+    ns: &mut Namespace,
+) -> Result<(u16, bool), ()> {
+    match l {
+        Type::Uint(n) => Ok((*n, false)),
+        Type::Int(n) => Ok((*n, true)),
+        Type::Bytes(n) if allow_bytes => Ok((*n as u16 * 8, false)),
+        Type::Enum(n) => {
+            ns.diagnostics.push(Output::error(
+                *l_loc,
+                format!("type enum {} not allowed", ns.enums[*n].print_to_string(),),
+            ));
+            Err(())
+        }
+        Type::Struct(n) => {
+            ns.diagnostics.push(Output::error(
+                *l_loc,
+                format!(
+                    "type struct {} not allowed",
+                    ns.structs[*n].print_to_string()
+                ),
+            ));
+            Err(())
+        }
+        Type::Array(_, _) => {
+            ns.diagnostics.push(Output::error(
+                *l_loc,
+                format!("type array {} not allowed", l.to_string(ns)),
+            ));
+            Err(())
+        }
+        Type::Ref(n) => get_int_length(n, l_loc, allow_bytes, ns),
+        Type::StorageRef(n) => get_int_length(n, l_loc, allow_bytes, ns),
+        _ => {
+            ns.diagnostics.push(Output::error(
+                *l_loc,
+                format!("expression of type {} not allowed", l.to_string(ns)),
+            ));
+            Err(())
+        }
+    }
+}
+
+fn coerce_int(
+    l: &Type,
+    l_loc: &pt::Loc,
+    r: &Type,
+    r_loc: &pt::Loc,
+    allow_bytes: bool,
+    ns: &mut Namespace,
+) -> Result<Type, ()> {
+    let l = match l {
+        Type::Ref(ty) => ty,
+        Type::StorageRef(ty) => ty,
+        _ => l,
+    };
+    let r = match r {
+        Type::Ref(ty) => ty,
+        Type::StorageRef(ty) => ty,
+        _ => r,
+    };
+
+    match (l, r) {
+        (Type::Bytes(left_length), Type::Bytes(right_length)) if allow_bytes => {
+            return Ok(Type::Bytes(std::cmp::max(*left_length, *right_length)));
+        }
+        _ => (),
+    }
+
+    let (left_len, left_signed) = get_int_length(l, l_loc, false, ns)?;
+
+    let (right_len, right_signed) = get_int_length(r, r_loc, false, ns)?;
+
+    Ok(match (left_signed, right_signed) {
+        (true, true) => Type::Int(cmp::max(left_len, right_len)),
+        (false, false) => Type::Uint(cmp::max(left_len, right_len)),
+        (true, false) => Type::Int(cmp::max(left_len, cmp::min(right_len + 8, 256))),
+        (false, true) => Type::Int(cmp::max(cmp::min(left_len + 8, 256), right_len)),
+    })
+}
+
+/// Try to convert a BigInt into a Expression::NumberLiteral. This checks for sign,
+/// width and creates to correct Type.
+fn bigint_to_expression(loc: &pt::Loc, n: &BigInt, ns: &mut Namespace) -> Result<Expression, ()> {
+    try_bigint_to_expression(loc, n).map_err(|d| {
+        ns.diagnostics.push(d);
+    })
+}
+
+pub fn try_bigint_to_expression(loc: &pt::Loc, n: &BigInt) -> Result<Expression, Output> {
+    // Return smallest type
+    let bits = n.bits();
+
+    let int_size = if bits < 7 { 8 } else { (bits + 7) & !7 } as u16;
+
+    if n.sign() == Sign::Minus {
+        if bits > 255 {
+            Err(Output::error(*loc, format!("{} is too large", n)))
+        } else {
+            Ok(Expression::NumberLiteral(
+                *loc,
+                Type::Int(int_size),
+                n.clone(),
+            ))
+        }
+    } else if bits > 256 {
+        Err(Output::error(*loc, format!("{} is too large", n)))
+    } else {
+        Ok(Expression::NumberLiteral(
+            *loc,
+            Type::Uint(int_size),
+            n.clone(),
+        ))
+    }
+}
+
+pub fn cast(
+    loc: &pt::Loc,
+    expr: Expression,
+    to: &Type,
+    implicit: bool,
+    ns: &mut Namespace,
+) -> Result<Expression, ()> {
+    try_cast(loc, expr, to, implicit, ns).map_err(|diagnostic| {
+        ns.diagnostics.push(diagnostic);
+    })
+}
+
+/// Cast from one type to another, which also automatically derefs any Type::Ref() type.
+/// if the cast is explicit (e.g. bytes32(bar) then implicit should be set to false.
+pub fn try_cast(
+    loc: &pt::Loc,
+    expr: Expression,
+    to: &Type,
+    implicit: bool,
+    ns: &Namespace,
+) -> Result<Expression, Output> {
+    let from = expr.ty();
+
+    if &from == to {
+        return Ok(expr);
+    }
+
+    // First of all, if we have a ref then derefence it
+    if let Type::Ref(r) = from {
+        return try_cast(
+            loc,
+            Expression::Load(*loc, r.as_ref().clone(), Box::new(expr)),
+            to,
+            implicit,
+            ns,
+        );
+    }
+
+    // If it's a storage reference then load the value. The expr is the storage slot
+    if let Type::StorageRef(r) = from {
+        if let Expression::StorageBytesSubscript(_, _, _) = expr {
+            return Ok(expr);
+        } else {
+            return try_cast(
+                loc,
+                Expression::StorageLoad(*loc, *r, Box::new(expr)),
+                to,
+                implicit,
+                ns,
+            );
+        }
+    }
+
+    // Special case: when converting literal sign can change if it fits
+    match (&expr, &from, to) {
+        (&Expression::NumberLiteral(_, _, ref n), p, &Type::Uint(to_len)) if p.is_primitive() => {
+            return if n.sign() == Sign::Minus {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion cannot change negative number to {}",
+                        to.to_string(ns)
+                    ),
+                ))
+            } else if n.bits() >= to_len as usize {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion would truncate from {} to {}",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::NumberLiteral(
+                    *loc,
+                    Type::Uint(to_len),
+                    n.clone(),
+                ))
+            }
+        }
+        (&Expression::NumberLiteral(_, _, ref n), p, &Type::Int(to_len)) if p.is_primitive() => {
+            return if n.bits() >= to_len as usize {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion would truncate from {} to {}",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::NumberLiteral(
+                    *loc,
+                    Type::Int(to_len),
+                    n.clone(),
+                ))
+            }
+        }
+        // Literal strings can be implicitly lengthened
+        (&Expression::BytesLiteral(_, _, ref bs), p, &Type::Bytes(to_len)) if p.is_primitive() => {
+            return if bs.len() > to_len as usize {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion would truncate from {} to {}",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ))
+            } else {
+                let mut bs = bs.to_owned();
+
+                // Add zero's at the end as needed
+                bs.resize(to_len as usize, 0);
+
+                Ok(Expression::BytesLiteral(*loc, Type::Bytes(to_len), bs))
+            };
+        }
+        (&Expression::BytesLiteral(loc, _, ref init), _, &Type::DynamicBytes)
+        | (&Expression::BytesLiteral(loc, _, ref init), _, &Type::String) => {
+            return Ok(Expression::AllocDynamicArray(
+                loc,
+                to.clone(),
+                Box::new(Expression::NumberLiteral(
+                    loc,
+                    Type::Uint(32),
+                    BigInt::from(init.len()),
+                )),
+                Some(init.clone()),
+            ));
+        }
+        _ => (),
+    };
+
+    cast_types(loc, expr, from, to.clone(), implicit, ns)
+}
+
+/// Do casting between types (no literals)
+fn cast_types(
+    loc: &pt::Loc,
+    expr: Expression,
+    from: Type,
+    to: Type,
+    implicit: bool,
+    ns: &Namespace,
+) -> Result<Expression, Output> {
+    let address_bits = ns.address_length as u16 * 8;
+
+    #[allow(clippy::comparison_chain)]
+    match (&from, &to) {
+        (Type::Uint(from_width), Type::Enum(enum_no))
+        | (Type::Int(from_width), Type::Enum(enum_no)) => {
+            if implicit {
+                return Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion from {} to {} not allowed",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ));
+            }
+
+            let enum_ty = &ns.enums[*enum_no];
+
+            // TODO would be help to have current contract to resolve contract constants
+            if let Ok((_, big_number)) = eval_const_number(&expr, None, ns) {
+                if let Some(number) = big_number.to_usize() {
+                    if enum_ty.values.values().any(|(_, v)| *v == number) {
+                        return Ok(Expression::NumberLiteral(
+                            expr.loc(),
+                            to.clone(),
+                            big_number,
+                        ));
+                    }
+                }
+
+                return Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "enum {} has no value with ordinal {}",
+                        to.to_string(ns),
+                        big_number
+                    ),
+                ));
+            }
+
+            let to_width = enum_ty.ty.bits(ns);
+
+            // TODO needs runtime checks
+            match from_width.cmp(&to_width) {
+                Ordering::Greater => Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr))),
+                Ordering::Less => Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr))),
+                Ordering::Equal => Ok(Expression::Cast(*loc, to.clone(), Box::new(expr))),
+            }
+        }
+        (Type::Enum(enum_no), Type::Uint(to_width))
+        | (Type::Enum(enum_no), Type::Int(to_width)) => {
+            if implicit {
+                return Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion from {} to {} not allowed",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ));
+            }
+
+            let enum_ty = &ns.enums[*enum_no];
+            let from_width = enum_ty.ty.bits(ns);
+
+            match from_width.cmp(&to_width) {
+                Ordering::Greater => Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr))),
+                Ordering::Less => Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr))),
+                Ordering::Equal => Ok(Expression::Cast(*loc, to.clone(), Box::new(expr))),
+            }
+        }
+        (Type::Bytes(1), Type::Uint(8)) => Ok(expr),
+        (Type::Uint(8), Type::Bytes(1)) => Ok(expr),
+        (Type::Uint(from_len), Type::Uint(to_len)) => match from_len.cmp(&to_len) {
+            Ordering::Greater => {
+                if implicit {
+                    Err(Output::type_error(
+                        *loc,
+                        format!(
+                            "implicit conversion would truncate from {} to {}",
+                            from.to_string(ns),
+                            to.to_string(ns)
+                        ),
+                    ))
+                } else {
+                    Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
+                }
+            }
+            Ordering::Less => Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr))),
+            Ordering::Equal => Ok(Expression::Cast(*loc, to.clone(), Box::new(expr))),
+        },
+        (Type::Int(from_len), Type::Int(to_len)) => match from_len.cmp(&to_len) {
+            Ordering::Greater => {
+                if implicit {
+                    Err(Output::type_error(
+                        *loc,
+                        format!(
+                            "implicit conversion would truncate from {} to {}",
+                            from.to_string(ns),
+                            to.to_string(ns)
+                        ),
+                    ))
+                } else {
+                    Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
+                }
+            }
+            Ordering::Less => Ok(Expression::SignExt(*loc, to.clone(), Box::new(expr))),
+            Ordering::Equal => Ok(Expression::Cast(*loc, to.clone(), Box::new(expr))),
+        },
+        (Type::Uint(from_len), Type::Int(to_len)) if to_len > from_len => {
+            Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
+        }
+        (Type::Int(from_len), Type::Uint(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion would change sign from {} to {}",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ))
+            } else if from_len > to_len {
+                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
+            } else if from_len < to_len {
+                Ok(Expression::SignExt(*loc, to.clone(), Box::new(expr)))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        (Type::Uint(from_len), Type::Int(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion would change sign from {} to {}",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ))
+            } else if from_len > to_len {
+                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
+            } else if from_len < to_len {
+                Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // Casting int to address
+        (Type::Uint(from_len), Type::Address(_)) | (Type::Int(from_len), Type::Address(_)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion from {} to address not allowed",
+                        from.to_string(ns)
+                    ),
+                ))
+            } else if *from_len > address_bits {
+                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
+            } else if *from_len < address_bits {
+                Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // Casting int address to int
+        (Type::Address(_), Type::Uint(to_len)) | (Type::Address(_), Type::Int(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion to {} from address not allowed",
+                        from.to_string(ns)
+                    ),
+                ))
+            } else if *to_len < address_bits {
+                Ok(Expression::Trunc(*loc, to.clone(), Box::new(expr)))
+            } else if *to_len > address_bits {
+                Ok(Expression::ZeroExt(*loc, to.clone(), Box::new(expr)))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // Lengthing or shorting a fixed bytes array
+        (Type::Bytes(from_len), Type::Bytes(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion would truncate from {} to {}",
+                        from.to_string(ns),
+                        to.to_string(ns)
+                    ),
+                ))
+            } else if to_len > from_len {
+                let shift = (to_len - from_len) * 8;
+
+                Ok(Expression::ShiftLeft(
+                    *loc,
+                    to.clone(),
+                    Box::new(Expression::ZeroExt(*loc, to.clone(), Box::new(expr))),
+                    Box::new(Expression::NumberLiteral(
+                        *loc,
+                        Type::Uint(*to_len as u16 * 8),
+                        BigInt::from_u8(shift).unwrap(),
+                    )),
+                ))
+            } else {
+                let shift = (from_len - to_len) * 8;
+
+                Ok(Expression::Trunc(
+                    *loc,
+                    to.clone(),
+                    Box::new(Expression::ShiftRight(
+                        *loc,
+                        from.clone(),
+                        Box::new(expr),
+                        Box::new(Expression::NumberLiteral(
+                            *loc,
+                            Type::Uint(*from_len as u16 * 8),
+                            BigInt::from_u8(shift).unwrap(),
+                        )),
+                        false,
+                    )),
+                ))
+            }
+        }
+        // Explicit conversion from bytesN to int/uint only allowed with expliciy
+        // cast and if it is the same size (i.e. no conversion required)
+        (Type::Bytes(from_len), Type::Uint(to_len))
+        | (Type::Bytes(from_len), Type::Int(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else if *from_len as u16 * 8 != *to_len {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // Explicit conversion to bytesN from int/uint only allowed with expliciy
+        // cast and if it is the same size (i.e. no conversion required)
+        (Type::Uint(from_len), Type::Bytes(to_len))
+        | (Type::Int(from_len), Type::Bytes(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else if *to_len as u16 * 8 != *from_len {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // Explicit conversion from bytesN to address only allowed with expliciy
+        // cast and if it is the same size (i.e. no conversion required)
+        (Type::Bytes(from_len), Type::Address(_)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else if *from_len as usize != ns.address_length {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // Explicit conversion between contract and address is allowed
+        (Type::Address(false), Type::Address(true))
+        | (Type::Address(_), Type::Contract(_))
+        | (Type::Contract(_), Type::Address(_)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        // conversion from address payable to address is implicitly allowed (not vice versa)
+        (Type::Address(true), Type::Address(false)) => {
+            Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+        }
+        // Explicit conversion to bytesN from int/uint only allowed with expliciy
+        // cast and if it is the same size (i.e. no conversion required)
+        (Type::Address(_), Type::Bytes(to_len)) => {
+            if implicit {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "implicit conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else if *to_len as usize != ns.address_length {
+                Err(Output::type_error(
+                    *loc,
+                    format!(
+                        "conversion to {} from {} not allowed",
+                        to.to_string(ns),
+                        from.to_string(ns)
+                    ),
+                ))
+            } else {
+                Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+            }
+        }
+        (Type::String, Type::DynamicBytes) | (Type::DynamicBytes, Type::String) if !implicit => {
+            Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)))
+        }
+        // string conversions
+        /*
+        (Type::Bytes(_), Type::String) => Ok(Expression::Cast(*loc, to.clone(), Box::new(expr)),
+        (Type::String, Type::Bytes(to_len)) => {
+            if let Expression::BytesLiteral(_, from_str) = &expr {
+                if from_str.len() > to_len as usize {
+                    ns.diagnostics.push(Output::type_error(
+                        *loc,
+                        format!(
+                            "string of {} bytes is too long to fit into {}",
+                            from_str.len(),
+                            to.to_string(ns)
+                        ),
+                    ));
+                    return Err(());
+                }
+            }
+            Ok(Expression::Cast(*loc, to.clone(), Box::new(expr))
+        }
+        */
+        (Type::Void, _) => Err(Output::type_error(
+            *loc,
+            "function or method does not return a value".to_string(),
+        )),
+        _ => Err(Output::type_error(
+            *loc,
+            format!(
+                "conversion from {} to {} not possible",
+                from.to_string(ns),
+                to.to_string(ns)
+            ),
+        )),
+    }
+}
+
+pub fn expression(
+    expr: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    match expr {
+        pt::Expression::ArrayLiteral(loc, exprs) => {
+            resolve_array_literal(loc, exprs, contract_no, ns, symtable, is_constant)
+        }
+        pt::Expression::BoolLiteral(loc, v) => Ok(Expression::BoolLiteral(*loc, *v)),
+        pt::Expression::StringLiteral(v) => {
+            // Concatenate the strings
+            let mut result = Vec::new();
+            let mut loc = pt::Loc(v[0].loc.0, 0);
+
+            for s in v {
+                result.extend_from_slice(unescape(&s.string, s.loc.0, ns).as_bytes());
+                loc.1 = s.loc.1;
+            }
+
+            let length = result.len();
+
+            Ok(Expression::BytesLiteral(
+                loc,
+                Type::Bytes(length as u8),
+                result,
+            ))
+        }
+        pt::Expression::HexLiteral(v) => {
+            let mut result = Vec::new();
+            let mut loc = pt::Loc(0, 0);
+
+            for s in v {
+                if (s.hex.len() % 2) != 0 {
+                    ns.diagnostics.push(Output::error(
+                        s.loc,
+                        format!("hex string \"{}\" has odd number of characters", s.hex),
+                    ));
+                    return Err(());
+                } else {
+                    result.extend_from_slice(&hex::decode(&s.hex).unwrap());
+                    if loc.0 == 0 {
+                        loc.0 = s.loc.0;
+                    }
+                    loc.1 = s.loc.1;
+                }
+            }
+
+            let length = result.len();
+
+            Ok(Expression::BytesLiteral(
+                loc,
+                Type::Bytes(length as u8),
+                result,
+            ))
+        }
+        pt::Expression::NumberLiteral(loc, b) => bigint_to_expression(loc, b, ns),
+        pt::Expression::HexNumberLiteral(loc, n) => {
+            // ns.address_length is in bytes; double for hex and two for the leading 0x
+            let looks_like_address = n.len() == ns.address_length * 2 + 2
+                && n.starts_with("0x")
+                && !n.chars().any(|c| c == '_');
+
+            if looks_like_address {
+                let address = to_hexstr_eip55(n);
+
+                if address == *n {
+                    let s: String = address.chars().skip(2).collect();
+
+                    Ok(Expression::NumberLiteral(
+                        *loc,
+                        Type::Address(false),
+                        BigInt::from_str_radix(&s, 16).unwrap(),
+                    ))
+                } else {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        format!(
+                            "address literal has incorrect checksum, expected ‘{}’",
+                            address
+                        ),
+                    ));
+                    Err(())
+                }
+            } else {
+                // from_str_radix does not like the 0x prefix
+                let s: String = n.chars().filter(|v| *v != 'x' && *v != '_').collect();
+
+                bigint_to_expression(loc, &BigInt::from_str_radix(&s, 16).unwrap(), ns)
+            }
+        }
+        pt::Expression::Variable(id) => {
+            if let Some(v) = symtable.find(&id.name) {
+                return if is_constant {
+                    ns.diagnostics.push(Output::error(
+                        id.loc,
+                        format!("cannot read variable ‘{}’ in constant expression", id.name),
+                    ));
+                    Err(())
+                } else {
+                    Ok(Expression::Variable(id.loc, v.ty.clone(), v.pos))
+                };
+            }
+
+            let v = ns.resolve_var(contract_no.unwrap(), id)?;
+
+            let var = &ns.contracts[contract_no.unwrap()].variables[v];
+
+            match var.var {
+                ContractVariableType::Constant => {
+                    Ok(Expression::ConstantVariable(id.loc, var.ty.clone(), v))
+                }
+                ContractVariableType::Storage(_) => {
+                    if is_constant {
+                        ns.diagnostics.push(Output::error(
+                            id.loc,
+                            format!(
+                                "cannot read contract variable ‘{}’ in constant expression",
+                                id.name
+                            ),
+                        ));
+                        Err(())
+                    } else {
+                        Ok(Expression::StorageVariable(
+                            id.loc,
+                            Type::StorageRef(Box::new(var.ty.clone())),
+                            v,
+                        ))
+                    }
+                }
+            }
+        }
+        pt::Expression::Add(loc, l, r) => {
+            addition(loc, l, r, contract_no, ns, symtable, is_constant)
+        }
+        pt::Expression::Subtract(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), false, ns)?;
+
+            Ok(Expression::Subtract(
+                *loc,
+                ty.clone(),
+                Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+            ))
+        }
+        pt::Expression::BitwiseOr(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            Ok(Expression::BitwiseOr(
+                *loc,
+                ty.clone(),
+                Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+            ))
+        }
+        pt::Expression::BitwiseAnd(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            Ok(Expression::BitwiseAnd(
+                *loc,
+                ty.clone(),
+                Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+            ))
+        }
+        pt::Expression::BitwiseXor(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            Ok(Expression::BitwiseXor(
+                *loc,
+                ty.clone(),
+                Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+            ))
+        }
+        pt::Expression::ShiftLeft(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            // left hand side may be bytes/int/uint
+            // right hand size may be int/uint
+            let _ = get_int_length(&left.ty(), &l.loc(), true, ns)?;
+            let (right_length, _) = get_int_length(&right.ty(), &r.loc(), false, ns)?;
+
+            let left_type = left.ty();
+
+            Ok(Expression::ShiftLeft(
+                *loc,
+                left_type.clone(),
+                Box::new(left),
+                Box::new(cast_shift_arg(loc, right, right_length, &left_type, ns)),
+            ))
+        }
+        pt::Expression::ShiftRight(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let left_type = left.ty();
+            // left hand side may be bytes/int/uint
+            // right hand size may be int/uint
+            let _ = get_int_length(&left_type, &l.loc(), true, ns)?;
+            let (right_length, _) = get_int_length(&right.ty(), &r.loc(), false, ns)?;
+
+            Ok(Expression::ShiftRight(
+                *loc,
+                left_type.clone(),
+                Box::new(left),
+                Box::new(cast_shift_arg(loc, right, right_length, &left_type, ns)),
+                left_type.is_signed_int(),
+            ))
+        }
+        pt::Expression::Multiply(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), false, ns)?;
+
+            Ok(Expression::Multiply(
+                *loc,
+                ty.clone(),
+                Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+            ))
+        }
+        pt::Expression::Divide(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), false, ns)?;
+
+            if ty.is_signed_int() {
+                Ok(Expression::SDivide(
+                    *loc,
+                    ty.clone(),
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            } else {
+                Ok(Expression::UDivide(
+                    *loc,
+                    ty.clone(),
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            }
+        }
+        pt::Expression::Modulo(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), false, ns)?;
+
+            if ty.is_signed_int() {
+                Ok(Expression::SModulo(
+                    *loc,
+                    ty.clone(),
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            } else {
+                Ok(Expression::UModulo(
+                    *loc,
+                    ty.clone(),
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            }
+        }
+        pt::Expression::Power(loc, b, e) => {
+            let base = expression(b, contract_no, ns, symtable, is_constant)?;
+            let exp = expression(e, contract_no, ns, symtable, is_constant)?;
+
+            let base_type = base.ty();
+            let exp_type = exp.ty();
+
+            // solc-0.5.13 does not allow either base or exp to be signed
+            if base_type.is_signed_int() || exp_type.is_signed_int() {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "exponation (**) is not allowed with signed types".to_string(),
+                ));
+                return Err(());
+            }
+
+            let ty = coerce_int(&base_type, &b.loc(), &exp_type, &e.loc(), false, ns)?;
+
+            Ok(Expression::Power(
+                *loc,
+                ty.clone(),
+                Box::new(cast(&b.loc(), base, &ty, true, ns)?),
+                Box::new(cast(&e.loc(), exp, &ty, true, ns)?),
+            ))
+        }
+
+        // compare
+        pt::Expression::More(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            if ty.is_signed_int() {
+                Ok(Expression::SMore(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            } else {
+                Ok(Expression::UMore(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            }
+        }
+        pt::Expression::Less(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            if ty.is_signed_int() {
+                Ok(Expression::SLess(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            } else {
+                Ok(Expression::ULess(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            }
+        }
+        pt::Expression::MoreEqual(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            if ty.is_signed_int() {
+                Ok(Expression::SMoreEqual(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            } else {
+                Ok(Expression::UMoreEqual(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            }
+        }
+        pt::Expression::LessEqual(loc, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+            let ty = coerce_int(&left.ty(), &l.loc(), &right.ty(), &r.loc(), true, ns)?;
+
+            if ty.is_signed_int() {
+                Ok(Expression::SLessEqual(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            } else {
+                Ok(Expression::ULessEqual(
+                    *loc,
+                    Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+                    Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+                ))
+            }
+        }
+        pt::Expression::Equal(loc, l, r) => {
+            equal(loc, l, r, contract_no, ns, symtable, is_constant)
+        }
+
+        pt::Expression::NotEqual(loc, l, r) => Ok(Expression::Not(
+            *loc,
+            Box::new(equal(loc, l, r, contract_no, ns, symtable, is_constant)?),
+        )),
+        // unary expressions
+        pt::Expression::Not(loc, e) => {
+            let expr = expression(e, contract_no, ns, symtable, is_constant)?;
+
+            Ok(Expression::Not(
+                *loc,
+                Box::new(cast(&loc, expr, &Type::Bool, true, ns)?),
+            ))
+        }
+        pt::Expression::Complement(loc, e) => {
+            let expr = expression(e, contract_no, ns, symtable, is_constant)?;
+
+            let expr_ty = expr.ty();
+
+            get_int_length(&expr_ty, loc, true, ns)?;
+
+            Ok(Expression::Complement(*loc, expr_ty, Box::new(expr)))
+        }
+        pt::Expression::UnaryMinus(loc, e) => {
+            let expr = expression(e, contract_no, ns, symtable, is_constant)?;
+
+            let expr_type = expr.ty();
+
+            if let Expression::NumberLiteral(_, _, n) = expr {
+                bigint_to_expression(loc, &-n, ns)
+            } else {
+                get_int_length(&expr_type, loc, false, ns)?;
+
+                Ok(Expression::UnaryMinus(*loc, expr_type, Box::new(expr)))
+            }
+        }
+        pt::Expression::UnaryPlus(loc, e) => {
+            let expr = expression(e, contract_no, ns, symtable, is_constant)?;
+            let expr_type = expr.ty();
+
+            get_int_length(&expr_type, loc, false, ns)?;
+
+            Ok(expr)
+        }
+
+        pt::Expression::Ternary(loc, c, l, r) => {
+            let left = expression(l, contract_no, ns, symtable, is_constant)?;
+            let right = expression(r, contract_no, ns, symtable, is_constant)?;
+            let cond = expression(c, contract_no, ns, symtable, is_constant)?;
+
+            let cond = cast(&c.loc(), cond, &Type::Bool, true, ns)?;
+
+            let ty = coerce(&left.ty(), &l.loc(), &right.ty(), &r.loc(), ns)?;
+
+            Ok(Expression::Ternary(
+                *loc,
+                ty,
+                Box::new(cond),
+                Box::new(left),
+                Box::new(right),
+            ))
+        }
+
+        // pre/post decrement/increment
+        pt::Expression::PostIncrement(loc, var)
+        | pt::Expression::PreIncrement(loc, var)
+        | pt::Expression::PostDecrement(loc, var)
+        | pt::Expression::PreDecrement(loc, var) => {
+            if is_constant {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "operator not allowed in constant context".to_string(),
+                ));
+                return Err(());
+            };
+
+            incr_decr(var, expr, contract_no, ns, symtable)
+        }
+
+        // assignment
+        pt::Expression::Assign(loc, var, e) => {
+            if is_constant {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "assignment not allowed in constant context".to_string(),
+                ));
+                return Err(());
+            };
+
+            assign_single(loc, var, e, contract_no, ns, symtable)
+        }
+
+        pt::Expression::AssignAdd(loc, var, e)
+        | pt::Expression::AssignSubtract(loc, var, e)
+        | pt::Expression::AssignMultiply(loc, var, e)
+        | pt::Expression::AssignDivide(loc, var, e)
+        | pt::Expression::AssignModulo(loc, var, e)
+        | pt::Expression::AssignOr(loc, var, e)
+        | pt::Expression::AssignAnd(loc, var, e)
+        | pt::Expression::AssignXor(loc, var, e)
+        | pt::Expression::AssignShiftLeft(loc, var, e)
+        | pt::Expression::AssignShiftRight(loc, var, e) => {
+            if is_constant {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "assignment not allowed in constant context".to_string(),
+                ));
+                return Err(());
+            };
+
+            assign_expr(loc, var, expr, e, contract_no, ns, symtable)
+        }
+        pt::Expression::NamedFunctionCall(loc, ty, args) => {
+            let marker = ns.diagnostics.len();
+
+            // is it a struct literal
+            match ns.resolve_type(contract_no, true, ty) {
+                Ok(Type::Struct(n)) => {
+                    return named_struct_literal(
+                        loc,
+                        n,
+                        args,
+                        contract_no,
+                        ns,
+                        symtable,
+                        is_constant,
+                    );
+                }
+                Ok(_) => {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        "struct or function expected".to_string(),
+                    ));
+                    return Err(());
+                }
+                _ => {}
+            }
+
+            // not a struct literal, remove those errors and try resolving as function call
+            ns.diagnostics.truncate(marker);
+
+            if is_constant {
+                ns.diagnostics.push(Output::error(
+                    expr.loc(),
+                    "cannot call function in constant expression".to_string(),
+                ));
+                return Err(());
+            }
+
+            let expr = named_function_call_expr(loc, ty, args, contract_no, ns, symtable)?;
+
+            if expr.tys().len() > 1 {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "in expression context a function cannot return more than one value"
+                        .to_string(),
+                ));
+                return Err(());
+            }
+
+            Ok(expr)
+        }
+        pt::Expression::New(loc, call) => {
+            if is_constant {
+                ns.diagnostics.push(Output::error(
+                    expr.loc(),
+                    "new not allowed in constant expression".to_string(),
+                ));
+                return Err(());
+            }
+
+            match call.as_ref() {
+                pt::Expression::FunctionCall(_, ty, args) => {
+                    new(loc, ty, args, contract_no, ns, symtable)
+                }
+                pt::Expression::NamedFunctionCall(_, ty, args) => {
+                    constructor_named_args(loc, ty, args, contract_no, ns, symtable)
+                }
+                _ => unreachable!(),
+            }
+        }
+        pt::Expression::Delete(loc, _) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "delete not allowed in expression".to_string(),
+            ));
+            Err(())
+        }
+        pt::Expression::FunctionCall(loc, ty, args) => {
+            let marker = ns.diagnostics.len();
+
+            match ns.resolve_type(contract_no, true, ty) {
+                Ok(Type::Struct(n)) => {
+                    return struct_literal(loc, n, args, contract_no, ns, symtable, is_constant);
+                }
+                Ok(to) => {
+                    // Cast
+                    return if args.is_empty() {
+                        ns.diagnostics
+                            .push(Output::error(*loc, "missing argument to cast".to_string()));
+                        Err(())
+                    } else if args.len() > 1 {
+                        ns.diagnostics.push(Output::error(
+                            *loc,
+                            "too many arguments to cast".to_string(),
+                        ));
+                        Err(())
+                    } else {
+                        let expr = expression(&args[0], contract_no, ns, symtable, is_constant)?;
+
+                        cast(loc, expr, &to, false, ns)
+                    };
+                }
+                Err(_) => {
+                    ns.diagnostics.truncate(marker);
+                }
+            }
+
+            if is_constant {
+                ns.diagnostics.push(Output::error(
+                    expr.loc(),
+                    "cannot call function in constant expression".to_string(),
+                ));
+                return Err(());
+            }
+
+            let expr = function_call_expr(loc, ty, args, contract_no, ns, symtable)?;
+
+            if expr.tys().len() > 1 {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "in expression context a function cannot return more than one value"
+                        .to_string(),
+                ));
+                return Err(());
+            }
+
+            Ok(expr)
+        }
+        pt::Expression::ArraySubscript(loc, _, None) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "expected expression before ‘]’ token".to_string(),
+            ));
+
+            Err(())
+        }
+        pt::Expression::ArraySubscript(loc, array, Some(index)) => {
+            array_subscript(loc, array, index, contract_no, ns, symtable, is_constant)
+        }
+        pt::Expression::MemberAccess(loc, e, id) => {
+            member_access(loc, e, id, contract_no, ns, symtable, is_constant)
+        }
+        pt::Expression::Or(loc, left, right) => {
+            let boolty = Type::Bool;
+            let l = cast(
+                &loc,
+                expression(left, contract_no, ns, symtable, is_constant)?,
+                &boolty,
+                true,
+                ns,
+            )?;
+            let r = cast(
+                &loc,
+                expression(right, contract_no, ns, symtable, is_constant)?,
+                &boolty,
+                true,
+                ns,
+            )?;
+
+            Ok(Expression::Or(*loc, Box::new(l), Box::new(r)))
+        }
+        pt::Expression::And(loc, left, right) => {
+            let boolty = Type::Bool;
+            let l = cast(
+                &loc,
+                expression(left, contract_no, ns, symtable, is_constant)?,
+                &boolty,
+                true,
+                ns,
+            )?;
+            let r = cast(
+                &loc,
+                expression(right, contract_no, ns, symtable, is_constant)?,
+                &boolty,
+                true,
+                ns,
+            )?;
+
+            Ok(Expression::And(*loc, Box::new(l), Box::new(r)))
+        }
+        pt::Expression::Type(loc, _) => {
+            ns.diagnostics
+                .push(Output::error(*loc, "type not expected".to_owned()));
+            Err(())
+        }
+        pt::Expression::List(loc, _) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "lists only permitted in destructure statements".to_owned(),
+            ));
+            Err(())
+        }
+        pt::Expression::FunctionCallBlock(loc, _, _) => {
+            ns.diagnostics
+                .push(Output::error(*loc, "unexpect block encountered".to_owned()));
+            Err(())
+        }
+        pt::Expression::Unit(loc, expr, unit) => {
+            let n = match expr.as_ref() {
+                pt::Expression::NumberLiteral(_, n) => n,
+                pt::Expression::HexNumberLiteral(loc, _) => {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        "hexadecimal numbers cannot be used with unit denominations".to_owned(),
+                    ));
+                    return Err(());
+                }
+                _ => {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        "unit denominations can only be used with number literals".to_owned(),
+                    ));
+                    return Err(());
+                }
+            };
+
+            match unit {
+                pt::Unit::Wei(loc)
+                | pt::Unit::Finney(loc)
+                | pt::Unit::Szabo(loc)
+                | pt::Unit::Ether(loc)
+                    if ns.target != crate::Target::Ewasm =>
+                {
+                    ns.diagnostics.push(Output::warning(
+                        *loc,
+                        "ethereum currency unit used while not targetting ethereum".to_owned(),
+                    ));
+                }
+                _ => (),
+            }
+
+            bigint_to_expression(
+                loc,
+                &(n * match unit {
+                    pt::Unit::Seconds(_) => BigInt::from(1),
+                    pt::Unit::Minutes(_) => BigInt::from(60),
+                    pt::Unit::Hours(_) => BigInt::from(60 * 60),
+                    pt::Unit::Days(_) => BigInt::from(60 * 60 * 24),
+                    pt::Unit::Weeks(_) => BigInt::from(60 * 60 * 24 * 7),
+                    pt::Unit::Wei(_) => BigInt::from(1),
+                    pt::Unit::Szabo(_) => BigInt::from(10).pow(12u32),
+                    pt::Unit::Finney(_) => BigInt::from(10).pow(15u32),
+                    pt::Unit::Ether(_) => BigInt::from(10).pow(18u32),
+                }),
+                ns,
+            )
+        }
+        pt::Expression::This(loc) => match contract_no {
+            Some(contract_no) => Ok(Expression::GetAddress(*loc, Type::Contract(contract_no))),
+            None => {
+                ns.diagnostics.push(Output::warning(
+                    *loc,
+                    "this not allowed outside contract".to_owned(),
+                ));
+                Err(())
+            }
+        },
+    }
+}
+
+/// Resolve an new contract expression with positional arguments
+fn constructor(
+    loc: &pt::Loc,
+    no: usize,
+    args: &[pt::Expression],
+    call_args: CallArgs,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    // The current contract cannot be constructed with new. In order to create
+    // the contract, we need the code hash of the contract. Part of that code
+    // will be code we're emitted here. So we end up with a crypto puzzle.
+    let contract_no = match contract_no {
+        Some(n) if n == no => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "new cannot construct current contract ‘{}’",
+                    ns.contracts[contract_no.unwrap()].name
+                ),
+            ));
+            return Err(());
+        }
+        Some(n) => n,
+        None => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "new contract not allowed in this context".to_string(),
+            ));
+            return Err(());
+        }
+    };
+
+    // check for circular references
+    if circular_reference(no, contract_no, ns) {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "circular reference creating contract ‘{}’",
+                ns.contracts[no].name
+            ),
+        ));
+        return Err(());
+    }
+
+    if !ns.contracts[contract_no].creates.contains(&no) {
+        ns.contracts[contract_no].creates.push(no);
+    }
+
+    let mut resolved_args = Vec::new();
+
+    for arg in args {
+        let expr = expression(arg, Some(contract_no), ns, symtable, false)?;
+
+        resolved_args.push(Box::new(expr));
+    }
+
+    let marker = ns.diagnostics.len();
+
+    // constructor call
+    let mut constructor_no = 0;
+
+    for function_no in 0..ns.contracts[no].functions.len() {
+        if !ns.contracts[no].functions[function_no].is_constructor() {
+            continue;
+        }
+
+        let params_len = ns.contracts[no].functions[function_no].params.len();
+
+        if params_len != args.len() {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "constructor expects {} arguments, {} provided",
+                    params_len,
+                    args.len()
+                ),
+            ));
+            constructor_no += 1;
+            continue;
+        }
+
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        // check if arguments can be implicitly casted
+        for i in 0..params_len {
+            let arg = &resolved_args[i];
+
+            match cast(
+                &args[i].loc(),
+                *arg.clone(),
+                &ns.contracts[no].functions[function_no].params[i].ty.clone(),
+                true,
+                ns,
+            ) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+
+        if matches {
+            return Ok(Expression::Constructor {
+                loc: *loc,
+                contract_no: no,
+                constructor_no,
+                args: cast_args,
+                value: call_args.value,
+                gas: call_args.gas,
+                salt: call_args.salt,
+            });
+        }
+        constructor_no += 1;
+    }
+
+    match ns.contracts[no]
+        .functions
+        .iter()
+        .filter(|f| f.is_constructor())
+        .count()
+    {
+        0 => Ok(Expression::Constructor {
+            loc: *loc,
+            contract_no: no,
+            constructor_no: 0,
+            args: Vec::new(),
+            value: call_args.value,
+            gas: call_args.gas,
+            salt: call_args.salt,
+        }),
+        1 => Err(()),
+        _ => {
+            ns.diagnostics.truncate(marker);
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "cannot find overloaded constructor which matches signature".to_string(),
+            ));
+
+            Err(())
+        }
+    }
+}
+
+/// check if from creates to, recursively
+fn circular_reference(from: usize, to: usize, ns: &Namespace) -> bool {
+    if ns.contracts[from].creates.contains(&to) {
+        return true;
+    }
+
+    ns.contracts[from]
+        .creates
+        .iter()
+        .any(|n| circular_reference(*n, to, &ns))
+}
+
+/// Resolve an new contract expression with named arguments
+pub fn constructor_named_args(
+    loc: &pt::Loc,
+    ty: &pt::Expression,
+    args: &[pt::NamedArgument],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let (ty, call_args, _) = collect_call_args(ty, ns)?;
+
+    let call_args = parse_call_args(&call_args, false, contract_no, ns, symtable)?;
+
+    let no = match ns.resolve_type(contract_no, false, ty)? {
+        Type::Contract(n) => n,
+        _ => {
+            ns.diagnostics
+                .push(Output::error(*loc, "contract expected".to_string()));
+            return Err(());
+        }
+    };
+
+    // The current contract cannot be constructed with new. In order to create
+    // the contract, we need the code hash of the contract. Part of that code
+    // will be code we're emitted here. So we end up with a crypto puzzle.
+    let contract_no = match contract_no {
+        Some(n) if n == no => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "new cannot construct current contract ‘{}’",
+                    ns.contracts[contract_no.unwrap()].name
+                ),
+            ));
+            return Err(());
+        }
+        Some(n) => n,
+        None => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "new contract not allowed in this context".to_string(),
+            ));
+            return Err(());
+        }
+    };
+
+    // check for circular references
+    if circular_reference(no, contract_no, ns) {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "circular reference creating contract ‘{}’",
+                ns.contracts[no].name
+            ),
+        ));
+        return Err(());
+    }
+
+    if !ns.contracts[contract_no].creates.contains(&no) {
+        ns.contracts[contract_no].creates.push(no);
+    }
+
+    let mut arguments = HashMap::new();
+
+    for arg in args {
+        arguments.insert(
+            arg.name.name.to_string(),
+            expression(&arg.expr, Some(contract_no), ns, symtable, false)?,
+        );
+    }
+
+    let marker = ns.diagnostics.len();
+
+    let mut constructor_no = 0;
+
+    // constructor call
+    for function_no in 0..ns.contracts[no].functions.len() {
+        if !ns.contracts[no].functions[function_no].is_constructor() {
+            continue;
+        }
+
+        let params_len = ns.contracts[no].functions[function_no].params.len();
+
+        if params_len != args.len() {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "constructor expects {} arguments, {} provided",
+                    params_len,
+                    args.len()
+                ),
+            ));
+            constructor_no += 1;
+            continue;
+        }
+
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        // check if arguments can be implicitly casted
+        for i in 0..params_len {
+            let param = ns.contracts[no].functions[function_no].params[i].clone();
+            let arg = match arguments.get(&param.name) {
+                Some(a) => a,
+                None => {
+                    matches = false;
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        format!("missing argument ‘{}’ to constructor", param.name),
+                    ));
+                    break;
+                }
+            };
+
+            match cast(&pt::Loc(0, 0), arg.clone(), &param.ty, true, ns) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+
+        if matches {
+            return Ok(Expression::Constructor {
+                loc: *loc,
+                contract_no: no,
+                constructor_no,
+                args: cast_args,
+                value: call_args.value,
+                gas: call_args.gas,
+                salt: call_args.salt,
+            });
+        }
+        constructor_no += 1;
+    }
+
+    match ns.contracts[no]
+        .functions
+        .iter()
+        .filter(|f| f.is_constructor())
+        .count()
+    {
+        0 => Ok(Expression::Constructor {
+            loc: *loc,
+            contract_no: no,
+            constructor_no: 0,
+            args: Vec::new(),
+            value: call_args.value,
+            gas: call_args.gas,
+            salt: call_args.salt,
+        }),
+        1 => Err(()),
+        _ => {
+            ns.diagnostics.truncate(marker);
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "cannot find overloaded constructor which matches signature".to_string(),
+            ));
+
+            Err(())
+        }
+    }
+}
+
+/// Resolve type(x).foo
+pub fn type_name_expr(
+    loc: &pt::Loc,
+    args: &[pt::Expression],
+    field: &pt::Identifier,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+) -> Result<Expression, ()> {
+    if args.is_empty() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "missing argument to type()".to_string(),
+        ));
+        return Err(());
+    }
+
+    if args.len() > 1 {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!("got {} arguments to type(), only one expected", args.len(),),
+        ));
+        return Err(());
+    }
+
+    let ty = ns.resolve_type(contract_no, false, &args[0])?;
+
+    match (&ty, field.name.as_str()) {
+        (Type::Uint(_), "min") => bigint_to_expression(loc, &BigInt::zero(), ns),
+        (Type::Uint(bits), "max") => {
+            let max = BigInt::one().shl(*bits as usize).sub(1);
+            bigint_to_expression(loc, &max, ns)
+        }
+        (Type::Int(bits), "min") => {
+            let min = BigInt::zero().sub(BigInt::one().shl(*bits as usize - 1));
+            bigint_to_expression(loc, &min, ns)
+        }
+        (Type::Int(bits), "max") => {
+            let max = BigInt::one().shl(*bits as usize - 1).sub(1);
+            bigint_to_expression(loc, &max, ns)
+        }
+        (Type::Contract(n), "name") => Ok(Expression::BytesLiteral(
+            *loc,
+            Type::String,
+            ns.contracts[*n].name.as_bytes().to_vec(),
+        )),
+        (Type::Contract(no), "creationCode") | (Type::Contract(no), "runtimeCode") => {
+            let contract_no = match contract_no {
+                Some(contract_no) => contract_no,
+                None => {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        format!(
+                            "type().{} not permitted outside of contract code",
+                            field.name
+                        ),
+                    ));
+                    return Err(());
+                }
+            };
+
+            // check for circular references
+            if *no == contract_no {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "containing our own contract code for ‘{}’ would generate infinite size contract",
+                        ns.contracts[*no].name
+                    ),
+                ));
+                return Err(());
+            }
+
+            if circular_reference(*no, contract_no, ns) {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "circular reference creating contract code for ‘{}’",
+                        ns.contracts[*no].name
+                    ),
+                ));
+                return Err(());
+            }
+
+            if !ns.contracts[contract_no].creates.contains(no) {
+                ns.contracts[contract_no].creates.push(*no);
+            }
+
+            Ok(Expression::CodeLiteral(
+                *loc,
+                *no,
+                field.name == "runtimeCode",
+            ))
+        }
+        _ => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "type ‘{}’ does not have type function {}",
+                    ty.to_string(ns),
+                    field.name
+                ),
+            ));
+            Err(())
+        }
+    }
+}
+
+/// Resolve an new expression
+pub fn new(
+    loc: &pt::Loc,
+    ty: &pt::Expression,
+    args: &[pt::Expression],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let (ty, call_args, call_args_loc) = collect_call_args(ty, ns)?;
+
+    let ty = ns.resolve_type(contract_no, false, ty)?;
+
+    match &ty {
+        Type::Array(ty, dim) => {
+            if dim.last().unwrap().is_some() {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "new cannot allocate fixed array type ‘{}’",
+                        ty.to_string(ns)
+                    ),
+                ));
+                return Err(());
+            }
+
+            if let Type::Contract(_) = ty.as_ref() {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!("new cannot construct array of ‘{}’", ty.to_string(ns)),
+                ));
+                return Err(());
+            }
+        }
+        Type::String | Type::DynamicBytes => {}
+        Type::Contract(n) => {
+            let call_args = parse_call_args(&call_args, false, contract_no, ns, symtable)?;
+
+            return constructor(loc, *n, args, call_args, contract_no, ns, symtable);
+        }
+        _ => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!("new cannot allocate type ‘{}’", ty.to_string(ns)),
+            ));
+            return Err(());
+        }
+    };
+
+    if let Some(loc) = call_args_loc {
+        ns.diagnostics.push(Output::error(
+            loc,
+            "constructor arguments not permitted for allocation".to_string(),
+        ));
+        return Err(());
+    }
+
+    if args.len() != 1 {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "new dynamic array should have a single length argument".to_string(),
+        ));
+        return Err(());
+    }
+    let size_loc = args[0].loc();
+
+    let size_expr = expression(&args[0], contract_no, ns, symtable, false)?;
+    let size_ty = size_expr.ty();
+
+    let size_width = match &size_ty {
+        Type::Uint(n) => n,
+        _ => {
+            ns.diagnostics.push(Output::error(
+                size_loc,
+                format!(
+                    "new size argument must be unsigned integer, not ‘{}’",
+                    size_ty.to_string(ns)
+                ),
+            ));
+            return Err(());
+        }
+    };
+
+    // TODO: should we check an upper bound? Large allocations will fail anyway,
+    // and ethereum solidity does not check at compile time
+    let size = match size_width.cmp(&32) {
+        Ordering::Greater => Expression::Trunc(size_loc, Type::Uint(32), Box::new(size_expr)),
+        Ordering::Less => Expression::ZeroExt(size_loc, Type::Uint(32), Box::new(size_expr)),
+        Ordering::Equal => size_expr,
+    };
+
+    Ok(Expression::AllocDynamicArray(
+        *loc,
+        ty,
+        Box::new(size),
+        None,
+    ))
+}
+
+/// Test for equality; first check string equality, then integer equality
+fn equal(
+    loc: &pt::Loc,
+    l: &pt::Expression,
+    r: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let left = expression(l, contract_no, ns, symtable, is_constant)?;
+    let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+    // Comparing stringliteral against stringliteral
+    if let (Expression::BytesLiteral(_, _, l), Expression::BytesLiteral(_, _, r)) = (&left, &right)
+    {
+        return Ok(Expression::BoolLiteral(*loc, l == r));
+    }
+
+    let left_type = left.ty();
+    let right_type = right.ty();
+
+    // compare string against literal
+    match (&left, &right_type.deref_any()) {
+        (Expression::BytesLiteral(_, _, l), Type::String)
+        | (Expression::BytesLiteral(_, _, l), Type::DynamicBytes) => {
+            return Ok(Expression::StringCompare(
+                *loc,
+                StringLocation::RunTime(Box::new(cast(
+                    &r.loc(),
+                    right,
+                    &right_type.deref_any(),
+                    true,
+                    ns,
+                )?)),
+                StringLocation::CompileTime(l.clone()),
+            ));
+        }
+        _ => {}
+    }
+
+    match (&right, &left_type.deref_any()) {
+        (Expression::BytesLiteral(_, _, literal), Type::String)
+        | (Expression::BytesLiteral(_, _, literal), Type::DynamicBytes) => {
+            return Ok(Expression::StringCompare(
+                *loc,
+                StringLocation::RunTime(Box::new(cast(
+                    &l.loc(),
+                    left,
+                    &left_type.deref_any(),
+                    true,
+                    ns,
+                )?)),
+                StringLocation::CompileTime(literal.clone()),
+            ));
+        }
+        _ => {}
+    }
+
+    // compare string
+    match (&left_type.deref_any(), &right_type.deref_any()) {
+        (Type::String, Type::String) | (Type::DynamicBytes, Type::DynamicBytes) => {
+            return Ok(Expression::StringCompare(
+                *loc,
+                StringLocation::RunTime(Box::new(cast(
+                    &l.loc(),
+                    left,
+                    &left_type.deref_any(),
+                    true,
+                    ns,
+                )?)),
+                StringLocation::RunTime(Box::new(cast(
+                    &r.loc(),
+                    right,
+                    &right_type.deref_any(),
+                    true,
+                    ns,
+                )?)),
+            ));
+        }
+        _ => {}
+    }
+
+    let ty = coerce(&left_type, &l.loc(), &right_type, &r.loc(), ns)?;
+
+    Ok(Expression::Equal(
+        *loc,
+        Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+        Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+    ))
+}
+
+/// Try string concatenation
+fn addition(
+    loc: &pt::Loc,
+    l: &pt::Expression,
+    r: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let left = expression(l, contract_no, ns, symtable, is_constant)?;
+    let right = expression(r, contract_no, ns, symtable, is_constant)?;
+
+    // Concatenate stringliteral with stringliteral
+    if let (Expression::BytesLiteral(_, _, l), Expression::BytesLiteral(_, _, r)) = (&left, &right)
+    {
+        let mut c = Vec::with_capacity(l.len() + r.len());
+        c.extend_from_slice(l);
+        c.extend_from_slice(r);
+        let length = c.len();
+        return Ok(Expression::BytesLiteral(*loc, Type::Bytes(length as u8), c));
+    }
+
+    let left_type = left.ty();
+    let right_type = right.ty();
+
+    // compare string against literal
+    match (&left, &right_type) {
+        (Expression::BytesLiteral(_, _, l), Type::String)
+        | (Expression::BytesLiteral(_, _, l), Type::DynamicBytes) => {
+            return Ok(Expression::StringConcat(
+                *loc,
+                right_type,
+                StringLocation::CompileTime(l.clone()),
+                StringLocation::RunTime(Box::new(right)),
+            ));
+        }
+        _ => {}
+    }
+
+    match (&right, &left_type) {
+        (Expression::BytesLiteral(_, _, l), Type::String)
+        | (Expression::BytesLiteral(_, _, l), Type::DynamicBytes) => {
+            return Ok(Expression::StringConcat(
+                *loc,
+                left_type,
+                StringLocation::RunTime(Box::new(left)),
+                StringLocation::CompileTime(l.clone()),
+            ));
+        }
+        _ => {}
+    }
+
+    // compare string
+    match (&left_type, &right_type) {
+        (Type::String, Type::String) | (Type::DynamicBytes, Type::DynamicBytes) => {
+            return Ok(Expression::StringConcat(
+                *loc,
+                right_type,
+                StringLocation::RunTime(Box::new(left)),
+                StringLocation::RunTime(Box::new(right)),
+            ));
+        }
+        _ => {}
+    }
+
+    let ty = coerce_int(&left_type, &l.loc(), &right_type, &r.loc(), false, ns)?;
+
+    Ok(Expression::Add(
+        *loc,
+        ty.clone(),
+        Box::new(cast(&l.loc(), left, &ty, true, ns)?),
+        Box::new(cast(&r.loc(), right, &ty, true, ns)?),
+    ))
+}
+
+/// Resolve an assignment
+pub fn assign_single(
+    loc: &pt::Loc,
+    left: &pt::Expression,
+    right: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let var = expression(left, contract_no, ns, symtable, false)?;
+    let var_ty = var.ty();
+    let val = expression(right, contract_no, ns, symtable, false)?;
+
+    match &var {
+        Expression::ConstantVariable(loc, _, n) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "cannot assign to constant ‘{}’",
+                    ns.contracts[contract_no.unwrap()].variables[*n].name
+                ),
+            ));
+            Err(())
+        }
+        Expression::StorageVariable(loc, ty, _) => Ok(Expression::Assign(
+            *loc,
+            ty.clone(),
+            Box::new(var.clone()),
+            Box::new(cast(&right.loc(), val, ty.deref_any(), true, ns)?),
+        )),
+        Expression::Variable(_, var_ty, _) => Ok(Expression::Assign(
+            *loc,
+            var_ty.clone(),
+            Box::new(var.clone()),
+            Box::new(cast(&right.loc(), val, var_ty, true, ns)?),
+        )),
+        _ => match &var_ty {
+            Type::Ref(r_ty) | Type::StorageRef(r_ty) => Ok(Expression::Assign(
+                *loc,
+                var_ty.clone(),
+                Box::new(var),
+                Box::new(cast(&right.loc(), val, r_ty, true, ns)?),
+            )),
+            _ => {
+                ns.diagnostics.push(Output::error(
+                    var.loc(),
+                    "expression is not assignable".to_string(),
+                ));
+                Err(())
+            }
+        },
+    }
+}
+
+/// Resolve an assignment with an operator
+fn assign_expr(
+    loc: &pt::Loc,
+    left: &pt::Expression,
+    expr: &pt::Expression,
+    right: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let set = expression(right, contract_no, ns, symtable, false)?;
+    let set_type = set.ty();
+
+    let op = |assign: Expression, ty: &Type, ns: &mut Namespace| -> Result<Expression, ()> {
+        let set = match expr {
+            pt::Expression::AssignShiftLeft(_, _, _)
+            | pt::Expression::AssignShiftRight(_, _, _) => {
+                let left_length = get_int_length(&ty, &loc, true, ns)?;
+                let right_length = get_int_length(&set_type, &left.loc(), false, ns)?;
+
+                // TODO: does shifting by negative value need compiletime/runtime check?
+                if left_length == right_length {
+                    set
+                } else if right_length < left_length && set_type.is_signed_int() {
+                    Expression::SignExt(*loc, ty.clone(), Box::new(set))
+                } else if right_length < left_length && !set_type.is_signed_int() {
+                    Expression::ZeroExt(*loc, ty.clone(), Box::new(set))
+                } else {
+                    Expression::Trunc(*loc, ty.clone(), Box::new(set))
+                }
+            }
+            _ => cast(&right.loc(), set, &ty, true, ns)?,
+        };
+
+        Ok(match expr {
+            pt::Expression::AssignAdd(_, _, _) => {
+                Expression::Add(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignSubtract(_, _, _) => {
+                Expression::Subtract(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignMultiply(_, _, _) => {
+                Expression::Multiply(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignOr(_, _, _) => {
+                Expression::BitwiseOr(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignAnd(_, _, _) => {
+                Expression::BitwiseAnd(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignXor(_, _, _) => {
+                Expression::BitwiseXor(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignShiftLeft(_, _, _) => {
+                Expression::ShiftLeft(*loc, ty.clone(), Box::new(assign), Box::new(set))
+            }
+            pt::Expression::AssignShiftRight(_, _, _) => Expression::ShiftRight(
+                *loc,
+                ty.clone(),
+                Box::new(assign),
+                Box::new(set),
+                ty.is_signed_int(),
+            ),
+            pt::Expression::AssignDivide(_, _, _) => {
+                if ty.is_signed_int() {
+                    Expression::SDivide(*loc, ty.clone(), Box::new(assign), Box::new(set))
+                } else {
+                    Expression::UDivide(*loc, ty.clone(), Box::new(assign), Box::new(set))
+                }
+            }
+            pt::Expression::AssignModulo(_, _, _) => {
+                if ty.is_signed_int() {
+                    Expression::SModulo(*loc, ty.clone(), Box::new(assign), Box::new(set))
+                } else {
+                    Expression::UModulo(*loc, ty.clone(), Box::new(assign), Box::new(set))
+                }
+            }
+            _ => unreachable!(),
+        })
+    };
+
+    let var = expression(left, contract_no, ns, symtable, false)?;
+    let var_ty = var.ty();
+
+    match &var {
+        Expression::ConstantVariable(loc, _, n) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "cannot assign to constant ‘{}’",
+                    ns.contracts[contract_no.unwrap()].variables[*n].name
+                ),
+            ));
+            Err(())
+        }
+        Expression::Variable(_, _, n) => {
+            match var_ty {
+                Type::Bytes(_) | Type::Int(_) | Type::Uint(_) => (),
+                _ => {
+                    ns.diagnostics.push(Output::error(
+                        var.loc(),
+                        format!(
+                            "variable ‘{}’ of incorrect type {}",
+                            symtable.get_name(*n),
+                            var_ty.to_string(ns)
+                        ),
+                    ));
+                    return Err(());
+                }
+            };
+            Ok(Expression::Assign(
+                *loc,
+                Type::Void,
+                Box::new(var.clone()),
+                Box::new(op(var, &var_ty, ns)?),
+            ))
+        }
+        _ => match &var_ty {
+            Type::Ref(r_ty) | Type::StorageRef(r_ty) => match r_ty.as_ref() {
+                Type::Bytes(_) | Type::Int(_) | Type::Uint(_) => Ok(Expression::Assign(
+                    *loc,
+                    Type::Void,
+                    Box::new(var.clone()),
+                    Box::new(op(cast(loc, var, r_ty, true, ns)?, r_ty, ns)?),
+                )),
+                _ => {
+                    ns.diagnostics.push(Output::error(
+                        var.loc(),
+                        format!("assigning to incorrect type {}", r_ty.to_string(ns)),
+                    ));
+                    Err(())
+                }
+            },
+            _ => {
+                ns.diagnostics.push(Output::error(
+                    var.loc(),
+                    "expression is not assignable".to_string(),
+                ));
+                Err(())
+            }
+        },
+    }
+}
+
+/// Resolve an increment/decrement with an operator
+fn incr_decr(
+    v: &pt::Expression,
+    expr: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let op = |e: Expression, ty: Type| -> Expression {
+        match expr {
+            pt::Expression::PreIncrement(loc, _) => Expression::PreIncrement(*loc, ty, Box::new(e)),
+            pt::Expression::PreDecrement(loc, _) => Expression::PreDecrement(*loc, ty, Box::new(e)),
+            pt::Expression::PostIncrement(loc, _) => {
+                Expression::PostIncrement(*loc, ty, Box::new(e))
+            }
+            pt::Expression::PostDecrement(loc, _) => {
+                Expression::PostDecrement(*loc, ty, Box::new(e))
+            }
+            _ => unreachable!(),
+        }
+    };
+
+    let var = expression(v, contract_no, ns, symtable, false)?;
+    let var_ty = var.ty();
+
+    match &var {
+        Expression::ConstantVariable(loc, _, n) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "cannot assign to constant ‘{}’",
+                    ns.contracts[contract_no.unwrap()].variables[*n].name
+                ),
+            ));
+            Err(())
+        }
+        Expression::Variable(_, ty, n) => {
+            match ty {
+                Type::Int(_) | Type::Uint(_) => (),
+                _ => {
+                    ns.diagnostics.push(Output::error(
+                        var.loc(),
+                        format!(
+                            "variable ‘{}’ of incorrect type {}",
+                            symtable.get_name(*n),
+                            var_ty.to_string(ns)
+                        ),
+                    ));
+                    return Err(());
+                }
+            };
+            Ok(op(var.clone(), ty.clone()))
+        }
+        _ => match &var_ty {
+            Type::Ref(r_ty) | Type::StorageRef(r_ty) => match r_ty.as_ref() {
+                Type::Int(_) | Type::Uint(_) => Ok(op(var, r_ty.as_ref().clone())),
+                _ => {
+                    ns.diagnostics.push(Output::error(
+                        var.loc(),
+                        format!("assigning to incorrect type {}", r_ty.to_string(ns)),
+                    ));
+                    Err(())
+                }
+            },
+            _ => {
+                ns.diagnostics.push(Output::error(
+                    var.loc(),
+                    "expression is not modifiable".to_string(),
+                ));
+                Err(())
+            }
+        },
+    }
+}
+
+/// Resolve an array subscript expression
+fn member_access(
+    loc: &pt::Loc,
+    e: &pt::Expression,
+    id: &pt::Identifier,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    // is of the form "contract_name.enum_name.enum_value"
+    if let pt::Expression::MemberAccess(_, e, enum_name) = e {
+        if let pt::Expression::Variable(contract_name) = e.as_ref() {
+            if let Some(contract_no) = ns.resolve_contract(contract_name) {
+                if let Some(e) = ns.resolve_enum(Some(contract_no), enum_name) {
+                    return match ns.enums[e].values.get(&id.name) {
+                        Some((_, val)) => Ok(Expression::NumberLiteral(
+                            *loc,
+                            Type::Enum(e),
+                            BigInt::from_usize(*val).unwrap(),
+                        )),
+                        None => {
+                            ns.diagnostics.push(Output::error(
+                                id.loc,
+                                format!(
+                                    "enum {} does not have value {}",
+                                    ns.enums[e].print_to_string(),
+                                    id.name
+                                ),
+                            ));
+                            Err(())
+                        }
+                    };
+                }
+            }
+        }
+    }
+
+    // is of the form "enum_name.enum_value"
+    if let pt::Expression::Variable(namespace) = e {
+        if let Some(e) = ns.resolve_enum(contract_no, namespace) {
+            return match ns.enums[e].values.get(&id.name) {
+                Some((_, val)) => Ok(Expression::NumberLiteral(
+                    *loc,
+                    Type::Enum(e),
+                    BigInt::from_usize(*val).unwrap(),
+                )),
+                None => {
+                    ns.diagnostics.push(Output::error(
+                        id.loc,
+                        format!(
+                            "enum {} does not have value {}",
+                            ns.enums[e].print_to_string(),
+                            id.name
+                        ),
+                    ));
+                    Err(())
+                }
+            };
+        }
+    }
+
+    // is of the form "type(x).field", like type(c).min
+    if let pt::Expression::FunctionCall(_, name, args) = e {
+        if let pt::Expression::Variable(func_name) = name.as_ref() {
+            if func_name.name == "type" {
+                return type_name_expr(loc, args, id, contract_no, ns);
+            }
+        }
+    }
+
+    let expr = expression(e, contract_no, ns, symtable, is_constant)?;
+    let expr_ty = expr.ty();
+
+    // Dereference if need to. This could be struct-in-struct for
+    // example.
+    let (expr, expr_ty) = if let Type::Ref(ty) = &expr_ty {
+        (
+            Expression::Load(*loc, expr_ty.clone(), Box::new(expr)),
+            ty.as_ref().clone(),
+        )
+    } else {
+        (expr, expr_ty)
+    };
+
+    match expr_ty {
+        Type::Bytes(n) => {
+            if id.name == "length" {
+                return Ok(Expression::NumberLiteral(
+                    *loc,
+                    Type::Uint(8),
+                    BigInt::from_u8(n).unwrap(),
+                ));
+            }
+        }
+        Type::Array(_, dim) => {
+            if id.name == "length" {
+                return match dim.last().unwrap() {
+                    None => Ok(Expression::DynamicArrayLength(*loc, Box::new(expr))),
+                    Some(d) => bigint_to_expression(loc, d, ns),
+                };
+            }
+        }
+        Type::String | Type::DynamicBytes => {
+            if id.name == "length" {
+                return Ok(Expression::DynamicArrayLength(*loc, Box::new(expr)));
+            }
+        }
+        Type::StorageRef(r) => match *r {
+            Type::Struct(n) => {
+                let mut slot = BigInt::zero();
+
+                for field in &ns.structs[n].fields {
+                    if id.name == field.name {
+                        return Ok(Expression::Add(
+                            *loc,
+                            Type::StorageRef(Box::new(field.ty.clone())),
+                            Box::new(expr),
+                            Box::new(Expression::NumberLiteral(*loc, Type::Uint(256), slot)),
+                        ));
+                    }
+
+                    slot += field.ty.storage_slots(ns);
+                }
+
+                ns.diagnostics.push(Output::error(
+                    id.loc,
+                    format!(
+                        "struct ‘{}’ does not have a field called ‘{}’",
+                        ns.structs[n].name, id.name
+                    ),
+                ));
+                return Err(());
+            }
+            Type::Bytes(n) => {
+                if id.name == "length" {
+                    return Ok(Expression::NumberLiteral(
+                        *loc,
+                        Type::Uint(8),
+                        BigInt::from_u8(n).unwrap(),
+                    ));
+                }
+            }
+            Type::Array(_, dim) => {
+                if id.name == "length" {
+                    return match dim.last().unwrap() {
+                        None => Ok(Expression::StorageLoad(
+                            id.loc,
+                            Type::Uint(256),
+                            Box::new(expr),
+                        )),
+                        Some(d) => bigint_to_expression(loc, d, ns),
+                    };
+                }
+            }
+            Type::DynamicBytes => {
+                if id.name == "length" {
+                    return Ok(Expression::StorageBytesLength(*loc, Box::new(expr)));
+                }
+            }
+            _ => {}
+        },
+        Type::Struct(n) => {
+            if let Some((i, f)) = ns.structs[n]
+                .fields
+                .iter()
+                .enumerate()
+                .find(|f| id.name == f.1.name)
+            {
+                return Ok(Expression::StructMember(
+                    *loc,
+                    Type::Ref(Box::new(f.ty.clone())),
+                    Box::new(expr),
+                    i,
+                ));
+            } else {
+                ns.diagnostics.push(Output::error(
+                    id.loc,
+                    format!(
+                        "struct ‘{}’ does not have a field called ‘{}’",
+                        ns.structs[n].print_to_string(),
+                        id.name
+                    ),
+                ));
+                return Err(());
+            }
+        }
+        Type::Address(_) => {
+            if id.name == "balance" {
+                if ns.target == crate::Target::Substrate {
+                    let mut is_this = false;
+
+                    if let Expression::Cast(_, _, this) = &expr {
+                        if let Expression::GetAddress(_, _) = this.as_ref() {
+                            is_this = true;
+                        }
+                    }
+
+                    if !is_this {
+                        ns.diagnostics.push(Output::error(
+                                    expr.loc(),
+                                        "substrate can only retrieve balance of this, like ‘address(this).balance’".to_string(),
+                                ));
+                        return Err(());
+                    }
+                }
+
+                return Ok(Expression::Balance(
+                    *loc,
+                    Type::Uint(ns.value_length as u16 * 8),
+                    Box::new(expr),
+                ));
+            }
+        }
+        _ => (),
+    }
+
+    ns.diagnostics
+        .push(Output::error(*loc, format!("‘{}’ not found", id.name)));
+
+    Err(())
+}
+
+/// Resolve an array subscript expression
+fn array_subscript(
+    loc: &pt::Loc,
+    array: &pt::Expression,
+    index: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let array_expr = expression(array, contract_no, ns, symtable, is_constant)?;
+    let array_ty = array_expr.ty();
+
+    if array_expr.ty().is_mapping() {
+        return mapping_subscript(
+            loc,
+            array_expr,
+            index,
+            contract_no,
+            ns,
+            symtable,
+            is_constant,
+        );
+    }
+
+    let index_expr = expression(index, contract_no, ns, symtable, is_constant)?;
+
+    match index_expr.ty() {
+        Type::Uint(_) => (),
+        _ => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "array subscript must be an unsigned integer, not ‘{}’",
+                    index_expr.ty().to_string(ns)
+                ),
+            ));
+            return Err(());
+        }
+    };
+
+    if array_ty.is_storage_bytes() {
+        return Ok(Expression::StorageBytesSubscript(
+            *loc,
+            Box::new(array_expr),
+            Box::new(cast(&index.loc(), index_expr, &Type::Uint(32), false, ns)?),
+        ));
+    }
+
+    match array_ty.deref_any() {
+        Type::Bytes(_) | Type::Array(_, _) | Type::DynamicBytes => {
+            if array_ty.is_contract_storage() {
+                Ok(Expression::ArraySubscript(
+                    *loc,
+                    array_ty.storage_array_elem(),
+                    Box::new(array_expr),
+                    Box::new(index_expr),
+                ))
+            } else {
+                Ok(Expression::ArraySubscript(
+                    *loc,
+                    array_ty.array_deref(),
+                    Box::new(cast(
+                        &array.loc(),
+                        array_expr,
+                        &array_ty.deref_any(),
+                        true,
+                        ns,
+                    )?),
+                    Box::new(index_expr),
+                ))
+            }
+        }
+        Type::String => {
+            ns.diagnostics.push(Output::error(
+                array.loc(),
+                "array subscript is not permitted on string".to_string(),
+            ));
+            Err(())
+        }
+        _ => {
+            ns.diagnostics.push(Output::error(
+                array.loc(),
+                "expression is not an array".to_string(),
+            ));
+            Err(())
+        }
+    }
+}
+
+/// Resolve a function call with positional arguments
+fn struct_literal(
+    loc: &pt::Loc,
+    struct_no: usize,
+    args: &[pt::Expression],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let struct_def = ns.structs[struct_no].clone();
+
+    if args.len() != struct_def.fields.len() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "struct ‘{}’ has {} fields, not {}",
+                struct_def.name,
+                struct_def.fields.len(),
+                args.len()
+            ),
+        ));
+        Err(())
+    } else {
+        let mut fields = Vec::new();
+
+        for (i, a) in args.iter().enumerate() {
+            let expr = expression(&a, contract_no, ns, symtable, is_constant)?;
+
+            fields.push(cast(loc, expr, &struct_def.fields[i].ty, true, ns)?);
+        }
+
+        let ty = Type::Struct(struct_no);
+
+        Ok(Expression::StructLiteral(*loc, ty, fields))
+    }
+}
+
+/// Resolve a function call with positional arguments
+fn function_call_pos_args(
+    loc: &pt::Loc,
+    id: &pt::Identifier,
+    args: &[pt::Expression],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let mut resolved_args = Vec::new();
+
+    for arg in args {
+        let expr = expression(arg, contract_no, ns, symtable, false)?;
+
+        resolved_args.push(expr);
+    }
+
+    // is it a builtin
+    if builtin::is_builtin_call(&id.name) {
+        let expr = builtin::resolve(loc, id, resolved_args, ns)?;
+
+        return if expr.tys().len() > 1 {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!("builtin function ‘{}’ returns more than one value", id.name),
+            ));
+            Err(())
+        } else {
+            Ok(expr)
+        };
+    }
+
+    // Try to resolve as a function call
+    let funcs = ns.resolve_func(contract_no.unwrap(), &id)?;
+
+    let marker = ns.diagnostics.len();
+
+    // function call
+    for (loc, function_no) in &funcs {
+        let params_len = ns.contracts[contract_no.unwrap()].functions[*function_no]
+            .params
+            .len();
+
+        if params_len != args.len() {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "function expects {} arguments, {} provided",
+                    params_len,
+                    args.len()
+                ),
+            ));
+            continue;
+        }
+
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        // check if arguments can be implicitly casted
+        for (i, arg) in resolved_args.iter().enumerate() {
+            match cast(
+                &arg.loc(),
+                arg.clone(),
+                &ns.contracts[contract_no.unwrap()].functions[*function_no].params[i]
+                    .ty
+                    .clone(),
+                true,
+                ns,
+            ) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+
+        if matches {
+            ns.diagnostics.truncate(marker);
+            let returns =
+                function_returns(&ns.contracts[contract_no.unwrap()].functions[*function_no]);
+
+            return Ok(Expression::InternalFunctionCall(
+                *loc,
+                returns,
+                *function_no,
+                cast_args,
+            ));
+        }
+    }
+
+    if funcs.len() != 1 {
+        ns.diagnostics.truncate(marker);
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "cannot find overloaded function which matches signature".to_string(),
+        ));
+    }
+
+    Err(())
+}
+
+/// Resolve a function call with named arguments
+fn function_call_with_named_args(
+    loc: &pt::Loc,
+    id: &pt::Identifier,
+    args: &[pt::NamedArgument],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    // Try to resolve as a function call
+    let funcs = ns.resolve_func(contract_no.unwrap(), &id)?;
+
+    let mut arguments = HashMap::new();
+
+    for arg in args {
+        if arguments.contains_key(&arg.name.name) {
+            ns.diagnostics.push(Output::error(
+                arg.name.loc,
+                format!("duplicate argument with name ‘{}’", arg.name.name),
+            ));
+            return Err(());
+        }
+
+        arguments.insert(
+            arg.name.name.to_string(),
+            expression(&arg.expr, contract_no, ns, symtable, false)?,
+        );
+    }
+
+    let marker = ns.diagnostics.len();
+
+    // function call
+    for (loc, function_no) in &funcs {
+        let params_len = ns.contracts[contract_no.unwrap()].functions[*function_no]
+            .params
+            .len();
+
+        if params_len != args.len() {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                format!(
+                    "function expects {} arguments, {} provided",
+                    params_len,
+                    args.len()
+                ),
+            ));
+            continue;
+        }
+
+        let mut matches = true;
+        let mut cast_args = Vec::new();
+
+        // check if arguments can be implicitly casted
+        for i in 0..params_len {
+            let param =
+                ns.contracts[contract_no.unwrap()].functions[*function_no].params[i].clone();
+
+            let arg = match arguments.get(&param.name) {
+                Some(a) => a,
+                None => {
+                    matches = false;
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        format!(
+                            "missing argument ‘{}’ to function ‘{}’",
+                            param.name, id.name,
+                        ),
+                    ));
+                    break;
+                }
+            };
+
+            match cast(&arg.loc(), arg.clone(), &param.ty, true, ns) {
+                Ok(expr) => cast_args.push(expr),
+                Err(()) => {
+                    matches = false;
+                    break;
+                }
+            }
+        }
+
+        if matches {
+            ns.diagnostics.truncate(marker);
+
+            let returns =
+                function_returns(&ns.contracts[contract_no.unwrap()].functions[*function_no]);
+
+            return Ok(Expression::InternalFunctionCall(
+                *loc,
+                returns,
+                *function_no,
+                cast_args,
+            ));
+        }
+    }
+
+    if funcs.len() != 1 {
+        ns.diagnostics.truncate(marker);
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "cannot find overloaded function which matches signature".to_string(),
+        ));
+    }
+
+    Err(())
+}
+
+/// Resolve a struct literal with named fields
+fn named_struct_literal(
+    loc: &pt::Loc,
+    struct_no: usize,
+    args: &[pt::NamedArgument],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let struct_def = ns.structs[struct_no].clone();
+
+    if args.len() != struct_def.fields.len() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "struct ‘{}’ has {} fields, not {}",
+                struct_def.name,
+                struct_def.fields.len(),
+                args.len()
+            ),
+        ));
+        Err(())
+    } else {
+        let mut fields = Vec::new();
+        fields.resize(args.len(), Expression::Poison);
+        for a in args {
+            match struct_def
+                .fields
+                .iter()
+                .enumerate()
+                .find(|(_, f)| f.name == a.name.name)
+            {
+                Some((i, f)) => {
+                    let expr = expression(&a.expr, contract_no, ns, symtable, is_constant)?;
+
+                    fields[i] = cast(loc, expr, &f.ty, true, ns)?;
+                }
+                None => {
+                    ns.diagnostics.push(Output::error(
+                        a.name.loc,
+                        format!(
+                            "struct ‘{}’ has no field ‘{}’",
+                            struct_def.name, a.name.name,
+                        ),
+                    ));
+                    return Err(());
+                }
+            }
+        }
+        let ty = Type::Struct(struct_no);
+        Ok(Expression::StructLiteral(*loc, ty, fields))
+    }
+}
+
+/// Resolve a method call with positional arguments
+fn method_call_pos_args(
+    loc: &pt::Loc,
+    var: &pt::Expression,
+    func: &pt::Identifier,
+    args: &[pt::Expression],
+    call_args: &[&pt::NamedArgument],
+    call_args_loc: Option<pt::Loc>,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let var_expr = expression(var, contract_no, ns, symtable, false)?;
+    let var_ty = var_expr.ty();
+
+    if let Type::StorageRef(ty) = &var_ty {
+        match ty.as_ref() {
+            Type::Array(_, dim) => {
+                if let Some(loc) = call_args_loc {
+                    ns.diagnostics.push(Output::error(
+                        loc,
+                        "call arguments not allowed on arrays".to_string(),
+                    ));
+                    return Err(());
+                }
+
+                if func.name == "push" {
+                    if dim.last().unwrap().is_some() {
+                        ns.diagnostics.push(Output::error(
+                            func.loc,
+                            "method ‘push()’ not allowed on fixed length array".to_string(),
+                        ));
+                        return Err(());
+                    }
+
+                    let elem_ty = ty.array_elem();
+                    let mut builtin_args = vec![var_expr];
+
+                    let ret_ty = match args.len() {
+                        1 => {
+                            let expr = expression(&args[0], contract_no, ns, symtable, false)?;
+
+                            builtin_args.push(cast(&args[0].loc(), expr, &elem_ty, true, ns)?);
+
+                            Type::Void
+                        }
+                        0 => {
+                            if elem_ty.is_reference_type() {
+                                Type::StorageRef(Box::new(elem_ty))
+                            } else {
+                                elem_ty
+                            }
+                        }
+                        _ => {
+                            ns.diagnostics.push(Output::error(
+                                func.loc,
+                                "method ‘push()’ takes at most 1 argument".to_string(),
+                            ));
+                            return Err(());
+                        }
+                    };
+
+                    return Ok(Expression::Builtin(
+                        *loc,
+                        vec![ret_ty],
+                        Builtin::ArrayPush,
+                        builtin_args,
+                    ));
+                }
+                if func.name == "pop" {
+                    if dim.last().unwrap().is_some() {
+                        ns.diagnostics.push(Output::error(
+                            func.loc,
+                            "method ‘pop()’ not allowed on fixed length array".to_string(),
+                        ));
+
+                        return Err(());
+                    }
+
+                    if !args.is_empty() {
+                        ns.diagnostics.push(Output::error(
+                            func.loc,
+                            "method ‘pop()’ does not take any arguments".to_string(),
+                        ));
+                        return Err(());
+                    }
+
+                    let storage_elem = ty.storage_array_elem();
+                    let elem_ty = storage_elem.deref_any();
+
+                    return Ok(Expression::Builtin(
+                        *loc,
+                        vec![elem_ty.clone()],
+                        Builtin::ArrayPop,
+                        vec![var_expr],
+                    ));
+                }
+            }
+            Type::DynamicBytes => {
+                if let Some(loc) = call_args_loc {
+                    ns.diagnostics.push(Output::error(
+                        loc,
+                        "call arguments not allowed on bytes".to_string(),
+                    ));
+                    return Err(());
+                }
+
+                if func.name == "push" {
+                    let mut builtin_args = vec![var_expr];
+
+                    let elem_ty = Type::Bytes(1);
+
+                    let ret_ty = match args.len() {
+                        1 => {
+                            let expr = expression(&args[0], contract_no, ns, symtable, false)?;
+
+                            builtin_args.push(cast(&args[0].loc(), expr, &elem_ty, true, ns)?);
+
+                            Type::Void
+                        }
+                        0 => elem_ty,
+                        _ => {
+                            ns.diagnostics.push(Output::error(
+                                func.loc,
+                                "method ‘push()’ takes at most 1 argument".to_string(),
+                            ));
+                            return Err(());
+                        }
+                    };
+
+                    return Ok(Expression::Builtin(
+                        *loc,
+                        vec![ret_ty],
+                        Builtin::BytesPush,
+                        builtin_args,
+                    ));
+                }
+
+                if func.name == "pop" {
+                    if !args.is_empty() {
+                        ns.diagnostics.push(Output::error(
+                            func.loc,
+                            "method ‘pop()’ does not take any arguments".to_string(),
+                        ));
+                        return Err(());
+                    }
+
+                    return Ok(Expression::Builtin(
+                        *loc,
+                        vec![Type::Bytes(1)],
+                        Builtin::BytesPop,
+                        vec![var_expr],
+                    ));
+                }
+            }
+            _ => {}
+        }
+    }
+
+    if let Type::Contract(contract_no) = &var_ty.deref_any() {
+        let call_args = parse_call_args(call_args, true, Some(*contract_no), ns, symtable)?;
+
+        let mut resolved_args = Vec::new();
+
+        for arg in args {
+            let expr = expression(arg, Some(*contract_no), ns, symtable, false)?;
+            resolved_args.push(Box::new(expr));
+        }
+
+        let marker = ns.diagnostics.len();
+        let mut name_match = 0;
+
+        for function_no in 0..ns.contracts[*contract_no].functions.len() {
+            if func.name != ns.contracts[*contract_no].functions[function_no].name {
+                continue;
+            }
+
+            name_match += 1;
+
+            let params_len = ns.contracts[*contract_no].functions[function_no]
+                .params
+                .len();
+
+            if params_len != args.len() {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "function expects {} arguments, {} provided",
+                        params_len,
+                        args.len()
+                    ),
+                ));
+                continue;
+            }
+            let mut matches = true;
+            let mut cast_args = Vec::new();
+            // check if arguments can be implicitly casted
+            for (i, arg) in resolved_args.iter().enumerate() {
+                match cast(
+                    &pt::Loc(0, 0),
+                    *arg.clone(),
+                    &ns.contracts[*contract_no].functions[function_no].params[i]
+                        .ty
+                        .clone(),
+                    true,
+                    ns,
+                ) {
+                    Ok(expr) => cast_args.push(expr),
+                    Err(()) => {
+                        matches = false;
+                        break;
+                    }
+                }
+            }
+            if matches {
+                ns.diagnostics.truncate(marker);
+
+                if !ns.contracts[*contract_no].functions[function_no].is_public() {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        format!("function ‘{}’ is not ‘public’ or ‘extern’", func.name),
+                    ));
+                    return Err(());
+                }
+
+                let value = if let Some(value) = call_args.value {
+                    if !value.const_zero(Some(*contract_no), ns)
+                        && !ns.contracts[*contract_no].functions[function_no].is_payable()
+                    {
+                        ns.diagnostics.push(Output::error(
+                            *loc,
+                            format!(
+                                "sending value to function ‘{}’ which is not payable",
+                                func.name
+                            ),
+                        ));
+                        return Err(());
+                    }
+
+                    value
+                } else {
+                    Box::new(Expression::NumberLiteral(
+                        pt::Loc(0, 0),
+                        Type::Uint(ns.value_length as u16 * 8),
+                        BigInt::zero(),
+                    ))
+                };
+
+                let returns = function_returns(&ns.contracts[*contract_no].functions[function_no]);
+
+                return Ok(Expression::ExternalFunctionCall {
+                    loc: *loc,
+                    contract_no: *contract_no,
+                    function_no,
+                    returns,
+                    address: Box::new(cast(
+                        &var.loc(),
+                        var_expr,
+                        &Type::Contract(*contract_no),
+                        true,
+                        ns,
+                    )?),
+                    args: cast_args,
+                    value,
+                    gas: call_args.gas,
+                });
+            }
+        }
+
+        if name_match != 1 {
+            ns.diagnostics.truncate(marker);
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "cannot find overloaded function which matches signature".to_string(),
+            ));
+        }
+
+        return Err(());
+    }
+
+    if let Type::Address(true) = &var_ty.deref_any() {
+        if func.name == "transfer" || func.name == "send" {
+            if args.len() != 1 {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "‘{}’ expects 1 argument, {} provided",
+                        func.name,
+                        args.len()
+                    ),
+                ));
+
+                return Err(());
+            }
+
+            if let Some(loc) = call_args_loc {
+                ns.diagnostics.push(Output::error(
+                    loc,
+                    format!("call arguments not allowed on ‘{}’", func.name),
+                ));
+                return Err(());
+            }
+
+            let expr = expression(&args[0], contract_no, ns, symtable, false)?;
+
+            let value = cast(
+                &args[0].loc(),
+                expr,
+                &Type::Uint(ns.value_length as u16 * 8),
+                true,
+                ns,
+            )?;
+
+            return if func.name == "transfer" {
+                Ok(Expression::Builtin(
+                    *loc,
+                    vec![Type::Void],
+                    Builtin::PayableTransfer,
+                    vec![var_expr, value],
+                ))
+            } else {
+                Ok(Expression::Builtin(
+                    *loc,
+                    vec![Type::Bool],
+                    Builtin::PayableSend,
+                    vec![var_expr, value],
+                ))
+            };
+        }
+    }
+
+    ns.diagnostics.push(Output::error(
+        func.loc,
+        format!("method ‘{}’ does not exist", func.name),
+    ));
+
+    Err(())
+}
+
+fn method_call_named_args(
+    loc: &pt::Loc,
+    var: &pt::Expression,
+    func_name: &pt::Identifier,
+    args: &[pt::NamedArgument],
+    call_args: &[&pt::NamedArgument],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let var_expr = expression(var, contract_no, ns, symtable, false)?;
+    let var_ty = var_expr.ty();
+
+    if let Type::Contract(external_contract_no) = &var_ty.deref_any() {
+        let call_args = parse_call_args(&call_args, true, contract_no, ns, symtable)?;
+
+        let mut arguments = HashMap::new();
+
+        for arg in args {
+            if arguments.contains_key(&arg.name.name) {
+                ns.diagnostics.push(Output::error(
+                    arg.name.loc,
+                    format!("duplicate argument with name ‘{}’", arg.name.name),
+                ));
+                return Err(());
+            }
+
+            arguments.insert(
+                arg.name.name.to_string(),
+                expression(&arg.expr, contract_no, ns, symtable, false)?,
+            );
+        }
+
+        let marker = ns.diagnostics.len();
+        let mut name_match = 0;
+
+        // function call
+        for function_no in 0..ns.contracts[*external_contract_no].functions.len() {
+            if ns.contracts[*external_contract_no].functions[function_no].name != func_name.name {
+                continue;
+            }
+
+            let params_len = ns.contracts[*external_contract_no].functions[function_no]
+                .params
+                .len();
+
+            name_match += 1;
+
+            if params_len != args.len() {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "function expects {} arguments, {} provided",
+                        params_len,
+                        args.len()
+                    ),
+                ));
+                continue;
+            }
+            let mut matches = true;
+            let mut cast_args = Vec::new();
+            // check if arguments can be implicitly casted
+            for i in 0..params_len {
+                let param =
+                    ns.contracts[*external_contract_no].functions[function_no].params[i].clone();
+
+                let arg = match arguments.get(&param.name) {
+                    Some(a) => a,
+                    None => {
+                        matches = false;
+                        ns.diagnostics.push(Output::error(
+                            *loc,
+                            format!(
+                                "missing argument ‘{}’ to function ‘{}’",
+                                param.name, func_name.name,
+                            ),
+                        ));
+                        break;
+                    }
+                };
+                match cast(&pt::Loc(0, 0), arg.clone(), &param.ty, true, ns) {
+                    Ok(expr) => cast_args.push(expr),
+                    Err(()) => {
+                        matches = false;
+                        break;
+                    }
+                }
+            }
+
+            if matches {
+                if !ns.contracts[*external_contract_no].functions[function_no].is_public() {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        format!("function ‘{}’ is not ‘public’ or ‘extern’", func_name.name),
+                    ));
+                    return Err(());
+                }
+
+                let value = if let Some(value) = call_args.value {
+                    if !value.const_zero(contract_no, ns)
+                        && !ns.contracts[*external_contract_no].functions[function_no].is_payable()
+                    {
+                        ns.diagnostics.push(Output::error(
+                            *loc,
+                            format!(
+                                "sending value to function ‘{}’ which is not payable",
+                                func_name.name
+                            ),
+                        ));
+                        return Err(());
+                    }
+
+                    value
+                } else {
+                    Box::new(Expression::NumberLiteral(
+                        pt::Loc(0, 0),
+                        Type::Uint(ns.value_length as u16 * 8),
+                        BigInt::zero(),
+                    ))
+                };
+
+                let returns =
+                    function_returns(&ns.contracts[*external_contract_no].functions[function_no]);
+
+                return Ok(Expression::ExternalFunctionCall {
+                    loc: *loc,
+                    contract_no: *external_contract_no,
+                    function_no,
+                    returns,
+                    address: Box::new(cast(
+                        &var.loc(),
+                        var_expr,
+                        &Type::Contract(*external_contract_no),
+                        true,
+                        ns,
+                    )?),
+                    args: cast_args,
+                    value,
+                    gas: call_args.gas,
+                });
+            }
+        }
+
+        match name_match {
+            0 => {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "contract ‘{}’ does not have function ‘{}’",
+                        var_ty.deref_any().to_string(ns),
+                        func_name.name
+                    ),
+                ));
+            }
+            1 => {}
+            _ => {
+                ns.diagnostics.truncate(marker);
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    "cannot find overloaded function which matches signature".to_string(),
+                ));
+            }
+        }
+        return Err(());
+    }
+
+    ns.diagnostics.push(Output::error(
+        func_name.loc,
+        format!("method ‘{}’ does not exist", func_name.name),
+    ));
+
+    Err(())
+}
+
+// When generating shifts, llvm wants both arguments to have the same width. We want the
+// result of the shift to be left argument, so this function coercies the right argument
+// into the right length.
+pub fn cast_shift_arg(
+    loc: &pt::Loc,
+    expr: Expression,
+    from_width: u16,
+    ty: &Type,
+    ns: &Namespace,
+) -> Expression {
+    let to_width = ty.bits(ns);
+
+    if from_width == to_width {
+        expr
+    } else if from_width < to_width && ty.is_signed_int() {
+        Expression::SignExt(*loc, ty.clone(), Box::new(expr))
+    } else if from_width < to_width && !ty.is_signed_int() {
+        Expression::ZeroExt(*loc, ty.clone(), Box::new(expr))
+    } else {
+        Expression::Trunc(*loc, ty.clone(), Box::new(expr))
+    }
+}
+
+/// Given an parsed literal array, ensure that it is valid. All the elements in the array
+/// must of the same type. The array might be a multidimensional array; all the leaf nodes
+/// must match.
+fn resolve_array_literal(
+    loc: &pt::Loc,
+    exprs: &[pt::Expression],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let mut dims = Box::new(Vec::new());
+    let mut flattened = Vec::new();
+
+    check_subarrays(exprs, &mut Some(&mut dims), &mut flattened, ns)?;
+
+    if flattened.is_empty() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "array requires at least one element".to_string(),
+        ));
+        return Err(());
+    }
+
+    let mut flattened = flattened.iter();
+
+    // We follow the solidity scheme were everthing gets implicitly converted to the
+    // type of the first element
+    let first = expression(
+        flattened.next().unwrap(),
+        contract_no,
+        ns,
+        symtable,
+        is_constant,
+    )?;
+
+    let ty = first.ty();
+    let mut exprs = vec![first];
+
+    for e in flattened {
+        let mut other = expression(e, contract_no, ns, symtable, is_constant)?;
+
+        if other.ty() != ty {
+            other = cast(&e.loc(), other, &ty, true, ns)?;
+        }
+
+        exprs.push(other);
+    }
+
+    let aty = Type::Array(
+        Box::new(ty),
+        dims.iter()
+            .map(|n| Some(BigInt::from_u32(*n).unwrap()))
+            .collect::<Vec<Option<BigInt>>>(),
+    );
+
+    if is_constant {
+        Ok(Expression::ConstArrayLiteral(*loc, aty, *dims, exprs))
+    } else {
+        Ok(Expression::ArrayLiteral(*loc, aty, *dims, exprs))
+    }
+}
+
+/// Traverse the literal looking for sub arrays. Ensure that all the sub
+/// arrays are the same length, and returned a flattened array of elements
+fn check_subarrays<'a>(
+    exprs: &'a [pt::Expression],
+    dims: &mut Option<&mut Vec<u32>>,
+    flatten: &mut Vec<&'a pt::Expression>,
+    ns: &mut Namespace,
+) -> Result<(), ()> {
+    if let Some(pt::Expression::ArrayLiteral(_, first)) = exprs.get(0) {
+        // ensure all elements are array literals of the same length
+        check_subarrays(first, dims, flatten, ns)?;
+
+        for (i, e) in exprs.iter().enumerate().skip(1) {
+            if let pt::Expression::ArrayLiteral(_, other) = e {
+                if other.len() != first.len() {
+                    ns.diagnostics.push(Output::error(
+                        e.loc(),
+                        format!(
+                            "array elements should be identical, sub array {} has {} elements rather than {}", i + 1, other.len(), first.len()
+                        ),
+                    ));
+                    return Err(());
+                }
+                check_subarrays(other, &mut None, flatten, ns)?;
+            } else {
+                ns.diagnostics.push(Output::error(
+                    e.loc(),
+                    format!("array element {} should also be an array", i + 1),
+                ));
+                return Err(());
+            }
+        }
+    } else {
+        for (i, e) in exprs.iter().enumerate().skip(1) {
+            if let pt::Expression::ArrayLiteral(loc, _) = e {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "array elements should be of the type, element {} is unexpected array",
+                        i + 1
+                    ),
+                ));
+                return Err(());
+            }
+        }
+        flatten.extend(exprs);
+    }
+
+    if let Some(dims) = dims.as_deref_mut() {
+        dims.push(exprs.len() as u32);
+    }
+
+    Ok(())
+}
+
+/// Function call arguments
+pub fn collect_call_args<'a>(
+    expr: &'a pt::Expression,
+    ns: &mut Namespace,
+) -> Result<
+    (
+        &'a pt::Expression,
+        Vec<&'a pt::NamedArgument>,
+        Option<pt::Loc>,
+    ),
+    (),
+> {
+    let mut named_arguments = Vec::new();
+    let mut expr = expr;
+    let mut loc: Option<pt::Loc> = None;
+
+    while let pt::Expression::FunctionCallBlock(_, e, block) = expr {
+        match block.as_ref() {
+            pt::Statement::Args(_, args) => {
+                if let Some(l) = loc {
+                    loc = Some(pt::Loc(l.0, block.loc().1));
+                } else {
+                    loc = Some(block.loc());
+                }
+
+                named_arguments.extend(args);
+            }
+            pt::Statement::Block(_, s) if s.is_empty() => {
+                // {}
+                ns.diagnostics.push(Output::error(
+                    block.loc(),
+                    "missing call arguments".to_string(),
+                ));
+                return Err(());
+            }
+            _ => {
+                ns.diagnostics.push(Output::error(
+                    block.loc(),
+                    "code block found where list of call arguments expected, like ‘{gas: 5000}’"
+                        .to_string(),
+                ));
+                return Err(());
+            }
+        }
+
+        expr = e;
+    }
+
+    Ok((expr, named_arguments, loc))
+}
+
+struct CallArgs {
+    gas: Box<Expression>,
+    salt: Option<Box<Expression>>,
+    value: Option<Box<Expression>>,
+}
+
+/// Parse call arguments for external calls
+fn parse_call_args(
+    call_args: &[&pt::NamedArgument],
+    external_call: bool,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<CallArgs, ()> {
+    let mut args: HashMap<&String, &pt::NamedArgument> = HashMap::new();
+
+    for arg in call_args {
+        if let Some(prev) = args.get(&arg.name.name) {
+            ns.diagnostics.push(Output::error_with_note(
+                arg.loc,
+                format!("‘{}’ specified multiple times", arg.name.name),
+                prev.loc,
+                format!("location of previous declaration of ‘{}’", arg.name.name),
+            ));
+            return Err(());
+        }
+
+        args.insert(&arg.name.name, arg);
+    }
+
+    let mut res = CallArgs {
+        gas: Box::new(Expression::NumberLiteral(
+            pt::Loc(0, 0),
+            Type::Uint(64),
+            BigInt::zero(),
+        )),
+        value: None,
+        salt: None,
+    };
+
+    for arg in args.values() {
+        match arg.name.name.as_str() {
+            "value" => {
+                let expr = expression(&arg.expr, contract_no, ns, symtable, false)?;
+
+                let ty = Type::Uint(ns.value_length as u16 * 8);
+
+                res.value = Some(Box::new(cast(&arg.expr.loc(), expr, &ty, true, ns)?));
+            }
+            "gas" => {
+                let expr = expression(&arg.expr, contract_no, ns, symtable, false)?;
+
+                let ty = Type::Uint(64);
+
+                res.gas = Box::new(cast(&arg.expr.loc(), expr, &ty, true, ns)?);
+            }
+            "salt" => {
+                if external_call {
+                    ns.diagnostics.push(Output::error(
+                        arg.loc,
+                        "‘salt’ not valid for external calls".to_string(),
+                    ));
+                    return Err(());
+                }
+
+                let expr = expression(&arg.expr, contract_no, ns, symtable, false)?;
+
+                let ty = Type::Uint(256);
+
+                res.salt = Some(Box::new(cast(&arg.expr.loc(), expr, &ty, true, ns)?));
+            }
+            _ => {
+                ns.diagnostics.push(Output::error(
+                    arg.loc,
+                    format!("‘{}’ not a valid call parameter", arg.name.name),
+                ));
+                return Err(());
+            }
+        }
+    }
+
+    Ok(res)
+}
+
+/// Resolve function call
+pub fn function_call_expr(
+    loc: &pt::Loc,
+    ty: &pt::Expression,
+    args: &[pt::Expression],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let (ty, call_args, call_args_loc) = collect_call_args(ty, ns)?;
+
+    match ty {
+        pt::Expression::MemberAccess(_, member, func) => method_call_pos_args(
+            loc,
+            member,
+            func,
+            args,
+            &call_args,
+            call_args_loc,
+            contract_no,
+            ns,
+            symtable,
+        ),
+        pt::Expression::Variable(id) => {
+            if let Some(loc) = call_args_loc {
+                ns.diagnostics.push(Output::error(
+                    loc,
+                    "call arguments not permitted for internal calls".to_string(),
+                ));
+                return Err(());
+            }
+
+            function_call_pos_args(loc, &id, args, contract_no, ns, symtable)
+        }
+        pt::Expression::ArraySubscript(_, _, _) => {
+            ns.diagnostics
+                .push(Output::error(ty.loc(), "unexpected array type".to_string()));
+            Err(())
+        }
+        _ => {
+            ns.diagnostics.push(Output::error(
+                ty.loc(),
+                "expression not expected here".to_string(),
+            ));
+            Err(())
+        }
+    }
+}
+
+/// Resolve function call expression with named arguments
+pub fn named_function_call_expr(
+    loc: &pt::Loc,
+    ty: &pt::Expression,
+    args: &[pt::NamedArgument],
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+) -> Result<Expression, ()> {
+    let (ty, call_args, call_args_loc) = collect_call_args(ty, ns)?;
+
+    match ty {
+        pt::Expression::MemberAccess(_, member, func) => method_call_named_args(
+            loc,
+            member,
+            func,
+            args,
+            &call_args,
+            contract_no,
+            ns,
+            symtable,
+        ),
+        pt::Expression::Variable(id) => {
+            if let Some(loc) = call_args_loc {
+                ns.diagnostics.push(Output::error(
+                    loc,
+                    "call arguments not permitted for internal calls".to_string(),
+                ));
+                return Err(());
+            }
+
+            function_call_with_named_args(loc, &id, args, contract_no, ns, symtable)
+        }
+        pt::Expression::ArraySubscript(_, _, _) => {
+            ns.diagnostics
+                .push(Output::error(ty.loc(), "unexpected array type".to_string()));
+            Err(())
+        }
+        _ => {
+            ns.diagnostics.push(Output::error(
+                ty.loc(),
+                "expression not expected here".to_string(),
+            ));
+            Err(())
+        }
+    }
+}
+
+/// Get the return values for a function call
+fn function_returns(ftype: &Function) -> Vec<Type> {
+    if !ftype.returns.is_empty() {
+        ftype.returns.iter().map(|p| p.ty.clone()).collect()
+    } else if ftype.noreturn {
+        vec![Type::Unreachable]
+    } else {
+        vec![Type::Void]
+    }
+}
+
+/// Calculate storage subscript
+fn mapping_subscript(
+    loc: &pt::Loc,
+    mapping: Expression,
+    index: &pt::Expression,
+    contract_no: Option<usize>,
+    ns: &mut Namespace,
+    symtable: &Symtable,
+    is_constant: bool,
+) -> Result<Expression, ()> {
+    let ty = mapping.ty();
+
+    let (key_ty, value_ty) = match ty.deref_any() {
+        Type::Mapping(k, v) => (k, v),
+        _ => unreachable!(),
+    };
+
+    let index_expr = cast(
+        &index.loc(),
+        expression(index, contract_no, ns, symtable, is_constant)?,
+        key_ty,
+        true,
+        ns,
+    )?;
+
+    Ok(Expression::ArraySubscript(
+        *loc,
+        Type::StorageRef(value_ty.clone()),
+        Box::new(mapping),
+        Box::new(index_expr),
+    ))
+}

+ 52 - 53
src/resolver/functions.rs → src/sema/functions.rs

@@ -1,4 +1,4 @@
-use super::{FunctionDecl, Namespace, Parameter, Symbol, Type};
+use super::ast::{Function, Namespace, Parameter, Symbol, Type};
 use output::Output;
 use parser::pt;
 use Target;
@@ -8,7 +8,6 @@ pub fn function_decl(
     i: usize,
     contract_no: usize,
     ns: &mut Namespace,
-    errors: &mut Vec<Output>,
 ) -> bool {
     let mut success = true;
 
@@ -19,14 +18,14 @@ pub fn function_decl(
             // Function name cannot be the same as the contract name
             if let Some(n) = &f.name {
                 if n.name == ns.contracts[contract_no].name {
-                    errors.push(Output::error(
+                    ns.diagnostics.push(Output::error(
                         f.loc,
                         "function cannot have same name as the contract".to_string(),
                     ));
                     return false;
                 }
             } else {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     f.name_loc,
                     "function is missing a name. did you mean ‘fallback() extern {…}’ or ‘receive() extern {…}’?".to_string(),
                 ));
@@ -35,14 +34,14 @@ pub fn function_decl(
         }
         pt::FunctionTy::Constructor => {
             if !f.returns.is_empty() {
-                errors.push(Output::warning(
+                ns.diagnostics.push(Output::warning(
                     f.loc,
                     "constructor cannot have return values".to_string(),
                 ));
                 return false;
             }
             if f.name.is_some() {
-                errors.push(Output::warning(
+                ns.diagnostics.push(Output::warning(
                     f.loc,
                     "constructor cannot have a name".to_string(),
                 ));
@@ -51,21 +50,21 @@ pub fn function_decl(
         }
         pt::FunctionTy::Fallback | pt::FunctionTy::Receive => {
             if !f.returns.is_empty() {
-                errors.push(Output::warning(
+                ns.diagnostics.push(Output::warning(
                     f.loc,
                     format!("{} function cannot have return values", f.ty),
                 ));
                 success = false;
             }
             if !f.params.is_empty() {
-                errors.push(Output::warning(
+                ns.diagnostics.push(Output::warning(
                     f.loc,
                     format!("{} function cannot have parameters", f.ty),
                 ));
                 success = false;
             }
             if f.name.is_some() {
-                errors.push(Output::warning(
+                ns.diagnostics.push(Output::warning(
                     f.loc,
                     format!("{} function cannot have a name", f.ty),
                 ));
@@ -81,7 +80,7 @@ pub fn function_decl(
         match &a {
             pt::FunctionAttribute::StateMutability(m) => {
                 if let Some(e) = &mutability {
-                    errors.push(Output::error_with_note(
+                    ns.diagnostics.push(Output::error_with_note(
                         m.loc(),
                         format!("function redeclared `{}'", m.to_string()),
                         e.loc(),
@@ -95,7 +94,7 @@ pub fn function_decl(
             }
             pt::FunctionAttribute::Visibility(v) => {
                 if let Some(e) = &visibility {
-                    errors.push(Output::error_with_note(
+                    ns.diagnostics.push(Output::error_with_note(
                         v.loc(),
                         format!("function redeclared `{}'", v.to_string()),
                         e.loc(),
@@ -113,7 +112,8 @@ pub fn function_decl(
     let visibility = match visibility {
         Some(v) => v,
         None => {
-            errors.push(Output::error(f.loc, "no visibility specified".to_string()));
+            ns.diagnostics
+                .push(Output::error(f.loc, "no visibility specified".to_string()));
             success = false;
             // continue processing while assuming it's a public
             pt::Visibility::Public(pt::Loc(0, 0))
@@ -125,7 +125,7 @@ pub fn function_decl(
     let storage_allowed = match visibility {
         pt::Visibility::Internal(_) | pt::Visibility::Private(_) => {
             if let Some(pt::StateMutability::Payable(loc)) = mutability {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     loc,
                     "internal or private function cannot be payable".to_string(),
                 ));
@@ -136,9 +136,9 @@ pub fn function_decl(
         pt::Visibility::Public(_) | pt::Visibility::External(_) => false,
     };
 
-    let (params, params_success) = resolve_params(f, storage_allowed, contract_no, ns, errors);
+    let (params, params_success) = resolve_params(f, storage_allowed, contract_no, ns);
 
-    let (returns, returns_success) = resolve_returns(f, storage_allowed, contract_no, ns, errors);
+    let (returns, returns_success) = resolve_returns(f, storage_allowed, contract_no, ns);
 
     if !success || !returns_success || !params_success {
         return false;
@@ -149,7 +149,7 @@ pub fn function_decl(
         None => "".to_owned(),
     };
 
-    let fdecl = FunctionDecl::new(
+    let fdecl = Function::new(
         f.loc,
         name,
         f.doc.clone(),
@@ -170,7 +170,7 @@ pub fn function_decl(
                 .iter()
                 .find(|f| f.is_constructor())
             {
-                errors.push(Output::error_with_note(
+                ns.diagnostics.push(Output::error_with_note(
                     f.loc,
                     "constructor already defined".to_string(),
                     prev.loc,
@@ -186,7 +186,7 @@ pub fn function_decl(
                 .iter()
                 .find(|f| f.is_constructor() && f.is_payable() != payable)
             {
-                errors.push(Output::error_with_note(
+                ns.diagnostics.push(Output::error_with_note(
                     f.loc,
                     "all constructors should be defined ‘payable’ or not".to_string(),
                     prev.loc,
@@ -200,7 +200,7 @@ pub fn function_decl(
         match fdecl.visibility {
             pt::Visibility::Public(_) => (),
             _ => {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     f.loc,
                     "constructor function must be declared public".to_owned(),
                 ));
@@ -210,14 +210,14 @@ pub fn function_decl(
 
         match fdecl.mutability {
             Some(pt::StateMutability::Pure(loc)) => {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     loc,
                     "constructor cannot be declared pure".to_string(),
                 ));
                 return false;
             }
             Some(pt::StateMutability::View(loc)) => {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     loc,
                     "constructor cannot be declared view".to_string(),
                 ));
@@ -232,7 +232,7 @@ pub fn function_decl(
             .filter(|f| f.is_constructor())
         {
             if v.signature == fdecl.signature {
-                errors.push(Output::error_with_note(
+                ns.diagnostics.push(Output::error_with_note(
                     f.loc,
                     "constructor with this signature already exists".to_string(),
                     v.loc,
@@ -252,7 +252,7 @@ pub fn function_decl(
             .iter()
             .find(|o| o.ty == f.ty)
         {
-            errors.push(Output::error_with_note(
+            ns.diagnostics.push(Output::error_with_note(
                 f.loc,
                 format!("{} function already defined", f.ty),
                 prev.loc,
@@ -264,7 +264,7 @@ pub fn function_decl(
         if let pt::Visibility::External(_) = fdecl.visibility {
             // ok
         } else {
-            errors.push(Output::error(
+            ns.diagnostics.push(Output::error(
                 f.loc,
                 format!("{} function must be declared external", f.ty),
             ));
@@ -273,14 +273,14 @@ pub fn function_decl(
 
         if let Some(pt::StateMutability::Payable(_)) = fdecl.mutability {
             if f.ty == pt::FunctionTy::Fallback {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     f.loc,
                     format!("{} function must not be declare payable, use ‘receive() external payable’ instead", f.ty),
                 ));
                 return false;
             }
         } else if f.ty == pt::FunctionTy::Receive {
-            errors.push(Output::error(
+            ns.diagnostics.push(Output::error(
                 f.loc,
                 format!("{} function must be declared payable", f.ty),
             ));
@@ -299,7 +299,7 @@ pub fn function_decl(
             // check if signature already present
             for o in v.iter() {
                 if ns.contracts[contract_no].functions[o.1].signature == fdecl.signature {
-                    errors.push(Output::error_with_note(
+                    ns.diagnostics.push(Output::error_with_note(
                         f.loc,
                         "overloaded function with this signature already exist".to_string(),
                         o.0,
@@ -321,12 +321,7 @@ pub fn function_decl(
 
         ns.contracts[contract_no].functions.push(fdecl);
 
-        ns.add_symbol(
-            Some(contract_no),
-            id,
-            Symbol::Function(vec![(id.loc, pos)]),
-            errors,
-        );
+        ns.add_symbol(Some(contract_no), id, Symbol::Function(vec![(id.loc, pos)]));
 
         true
     }
@@ -338,26 +333,26 @@ fn resolve_params(
     storage_allowed: bool,
     contract_no: usize,
     ns: &mut Namespace,
-    errors: &mut Vec<Output>,
 ) -> (Vec<Parameter>, bool) {
     let mut params = Vec::new();
     let mut success = true;
 
-    for p in &f.params {
+    for (loc, p) in &f.params {
         let p = match p {
-            (_, Some(p)) => p,
-            (loc, None) => {
-                errors.push(Output::error(*loc, "missing parameter type".to_owned()));
+            Some(p) => p,
+            None => {
+                ns.diagnostics
+                    .push(Output::error(*loc, "missing parameter type".to_owned()));
                 success = false;
                 continue;
             }
         };
 
-        match ns.resolve_type(Some(contract_no), false, &p.ty, errors) {
+        match ns.resolve_type(Some(contract_no), false, &p.ty) {
             Ok(ty) => {
                 let ty = if !ty.can_have_data_location() {
                     if let Some(storage) = &p.storage {
-                        errors.push(Output::error(
+                        ns.diagnostics.push(Output::error(
                                 *storage.loc(),
                                 format!("data location ‘{}’ can only be specified for array, struct or mapping",
                                 storage)
@@ -370,7 +365,7 @@ fn resolve_params(
                     if storage_allowed {
                         Type::StorageRef(Box::new(ty))
                     } else {
-                        errors.push(Output::error(
+                        ns.diagnostics.push(Output::error(
                             loc,
                             "parameter of type ‘storage’ not allowed public or external functions"
                                 .to_string(),
@@ -379,7 +374,7 @@ fn resolve_params(
                         ty
                     }
                 } else if ty.contains_mapping(ns) {
-                    errors.push(Output::error(
+                    ns.diagnostics.push(Output::error(
                         p.ty.loc(),
                         "parameter with mapping type must be of type ‘storage’".to_string(),
                     ));
@@ -390,6 +385,7 @@ fn resolve_params(
                 };
 
                 params.push(Parameter {
+                    loc: *loc,
                     name: p
                         .name
                         .as_ref()
@@ -410,26 +406,26 @@ fn resolve_returns(
     storage_allowed: bool,
     contract_no: usize,
     ns: &mut Namespace,
-    errors: &mut Vec<Output>,
 ) -> (Vec<Parameter>, bool) {
     let mut returns = Vec::new();
     let mut success = true;
 
-    for r in &f.returns {
+    for (loc, r) in &f.returns {
         let r = match r {
-            (_, Some(p)) => p,
-            (loc, None) => {
-                errors.push(Output::error(*loc, "missing return type".to_owned()));
+            Some(r) => r,
+            None => {
+                ns.diagnostics
+                    .push(Output::error(*loc, "missing return type".to_owned()));
                 success = false;
                 continue;
             }
         };
 
-        match ns.resolve_type(Some(contract_no), false, &r.ty, errors) {
+        match ns.resolve_type(Some(contract_no), false, &r.ty) {
             Ok(ty) => {
                 let ty = if !ty.can_have_data_location() {
                     if let Some(storage) = &r.storage {
-                        errors.push(Output::error(
+                        ns.diagnostics.push(Output::error(
                                 *storage.loc(),
                                 format!("data location ‘{}’ can only be specified for array, struct or mapping",
                                 storage)
@@ -441,7 +437,7 @@ fn resolve_returns(
                 } else {
                     match r.storage {
                         Some(pt::StorageLocation::Calldata(loc)) => {
-                            errors.push(Output::error(
+                            ns.diagnostics.push(Output::error(
                                 loc,
                                 "data location ‘calldata’ can not be used for return types"
                                     .to_string(),
@@ -453,7 +449,7 @@ fn resolve_returns(
                             if storage_allowed {
                                 Type::StorageRef(Box::new(ty))
                             } else {
-                                errors.push(Output::error(
+                                ns.diagnostics.push(Output::error(
                                     loc,
                                     "return type of type ‘storage’ not allowed public or external functions"
                                         .to_string(),
@@ -464,7 +460,7 @@ fn resolve_returns(
                         }
                         _ => {
                             if ty.contains_mapping(ns) {
-                                errors.push(Output::error(
+                                ns.diagnostics.push(Output::error(
                                     r.ty.loc(),
                                     "return type containing mapping must be of type ‘storage’"
                                         .to_string(),
@@ -478,6 +474,7 @@ fn resolve_returns(
                 };
 
                 returns.push(Parameter {
+                    loc: *loc,
                     name: r
                         .name
                         .as_ref()
@@ -498,7 +495,7 @@ fn signatures() {
 
     let ns = Namespace::new(Target::Ewasm, 20);
 
-    let fdecl = FunctionDecl::new(
+    let fdecl = Function::new(
         pt::Loc(0, 0),
         "foo".to_owned(),
         vec![],
@@ -508,10 +505,12 @@ fn signatures() {
         pt::Visibility::Public(pt::Loc(0, 0)),
         vec![
             Parameter {
+                loc: pt::Loc(0, 0),
                 name: "".to_string(),
                 ty: Type::Uint(8),
             },
             Parameter {
+                loc: pt::Loc(0, 0),
                 name: "".to_string(),
                 ty: Type::Address(false),
             },

+ 772 - 0
src/sema/mod.rs

@@ -0,0 +1,772 @@
+use inkwell::OptimizationLevel;
+use num_bigint::BigInt;
+use num_traits::Signed;
+use num_traits::Zero;
+use output::{any_errors, Note, Output};
+use parser::pt;
+use std::collections::HashMap;
+use Target;
+
+mod address;
+pub mod ast;
+mod builtin;
+pub mod eval;
+pub mod expression;
+mod functions;
+mod statements;
+pub mod symtable;
+mod types;
+mod variables;
+
+use codegen::cfg::ControlFlowGraph;
+use emit;
+use sema::ast::Statement;
+use sema::eval::eval_const_number;
+use sema::expression::expression;
+use sema::symtable::Symtable;
+
+pub type ArrayDimension = Option<(pt::Loc, BigInt)>;
+
+pub fn sema(s: pt::SourceUnit, target: Target) -> ast::Namespace {
+    // first resolve all the types we can find
+    let mut ns = types::resolve(&s, target);
+
+    // give up if we failed
+    if any_errors(&ns.diagnostics) {
+        return ns;
+    }
+
+    // we need to resolve declarations first, so we call functions/constructors of
+    // contracts before they are declared
+    let mut contract_no = 0;
+    for part in &s.0 {
+        if let pt::SourceUnitPart::ContractDefinition(def) = part {
+            resolve_contract_declarations(def, contract_no, target, &mut ns);
+
+            contract_no += 1;
+        }
+    }
+
+    // Now we can resolve the bodies
+    let mut contract_no = 0;
+    for part in &s.0 {
+        if let pt::SourceUnitPart::ContractDefinition(def) = part {
+            resolve_contract_bodies(def, contract_no, &mut ns);
+
+            contract_no += 1;
+        }
+    }
+
+    ns
+}
+
+/// Resolve functions declarations, constructor declarations, and contract variables
+fn resolve_contract_declarations(
+    def: &pt::ContractDefinition,
+    contract_no: usize,
+    target: Target,
+    ns: &mut ast::Namespace,
+) -> bool {
+    ns.diagnostics.push(Output::info(
+        def.loc,
+        format!("found contract {}", def.name.name),
+    ));
+
+    let mut broken = false;
+
+    // resolve function signatures
+    for (i, parts) in def.parts.iter().enumerate() {
+        if let pt::ContractPart::FunctionDefinition(ref f) = parts {
+            if !functions::function_decl(f, i, contract_no, ns) {
+                broken = true;
+            }
+        }
+    }
+
+    // resolve state variables
+    if variables::contract_variables(&def, contract_no, ns) {
+        broken = true;
+    }
+
+    // Substrate requires one constructor. Ideally we do not create implict things
+    // in the ast, but this is required for abi generation which is done of the ast
+    if !ns.contracts[contract_no]
+        .functions
+        .iter()
+        .any(|f| f.is_constructor())
+        && target == Target::Substrate
+    {
+        let mut fdecl = ast::Function::new(
+            pt::Loc(0, 0),
+            "".to_owned(),
+            vec![],
+            pt::FunctionTy::Constructor,
+            None,
+            None,
+            pt::Visibility::Public(pt::Loc(0, 0)),
+            Vec::new(),
+            Vec::new(),
+            ns,
+        );
+
+        fdecl.body = vec![Statement::Return(pt::Loc(0, 0), Vec::new())];
+
+        ns.contracts[contract_no].functions.push(fdecl);
+    }
+
+    broken
+}
+
+fn resolve_contract_bodies(
+    def: &pt::ContractDefinition,
+    contract_no: usize,
+    ns: &mut ast::Namespace,
+) -> bool {
+    let mut broken = false;
+
+    // resolve function bodies
+    for f in 0..ns.contracts[contract_no].functions.len() {
+        if let Some(ast_index) = ns.contracts[contract_no].functions[f].ast_index {
+            if let pt::ContractPart::FunctionDefinition(ref ast_f) = def.parts[ast_index] {
+                if statements::resolve_function_body(ast_f, contract_no, f, ns).is_err() {
+                    broken = true;
+                }
+            }
+        }
+    }
+
+    broken
+}
+
+impl ast::Namespace {
+    pub fn new(target: Target, address_length: usize) -> Self {
+        ast::Namespace {
+            target,
+            enums: Vec::new(),
+            structs: Vec::new(),
+            contracts: Vec::new(),
+            address_length,
+            value_length: 16,
+            symbols: HashMap::new(),
+            diagnostics: Vec::new(),
+        }
+    }
+
+    /// Add symbol to symbol table; either returns true for success, or adds an appropriate error
+    pub fn add_symbol(
+        &mut self,
+        contract_no: Option<usize>,
+        id: &pt::Identifier,
+        symbol: ast::Symbol,
+    ) -> bool {
+        if builtin::is_builtin_call(&id.name) {
+            self.diagnostics.push(Output::error(
+                id.loc,
+                format!(
+                    "‘{}’ shadows name of a builtin function",
+                    id.name.to_string()
+                ),
+            ));
+
+            return false;
+        }
+
+        if let Some(sym) = self.symbols.get(&(contract_no, id.name.to_owned())) {
+            match sym {
+                ast::Symbol::Contract(c, _) => {
+                    self.diagnostics.push(Output::error_with_note(
+                        id.loc,
+                        format!(
+                            "{} is already defined as a contract name",
+                            id.name.to_string()
+                        ),
+                        *c,
+                        "location of previous definition".to_string(),
+                    ));
+                }
+                ast::Symbol::Enum(c, _) => {
+                    self.diagnostics.push(Output::error_with_note(
+                        id.loc,
+                        format!("{} is already defined as an enum", id.name.to_string()),
+                        *c,
+                        "location of previous definition".to_string(),
+                    ));
+                }
+                ast::Symbol::Struct(c, _) => {
+                    self.diagnostics.push(Output::error_with_note(
+                        id.loc,
+                        format!("{} is already defined as a struct", id.name.to_string()),
+                        *c,
+                        "location of previous definition".to_string(),
+                    ));
+                }
+                ast::Symbol::Variable(c, _) => {
+                    self.diagnostics.push(Output::error_with_note(
+                        id.loc,
+                        format!(
+                            "{} is already defined as a contract variable",
+                            id.name.to_string()
+                        ),
+                        *c,
+                        "location of previous definition".to_string(),
+                    ));
+                }
+                ast::Symbol::Function(v) => {
+                    self.diagnostics.push(Output::error_with_note(
+                        id.loc,
+                        format!("{} is already defined as a function", id.name.to_string()),
+                        v[0].0,
+                        "location of previous definition".to_string(),
+                    ));
+                }
+            }
+
+            return false;
+        }
+
+        // if there is nothing on the contract level, try top-level scope
+        if contract_no.is_some() {
+            if let Some(sym) = self.symbols.get(&(None, id.name.to_owned())) {
+                match sym {
+                    ast::Symbol::Contract(c, _) => {
+                        self.diagnostics.push(Output::warning_with_note(
+                            id.loc,
+                            format!(
+                                "{} is already defined as a contract name",
+                                id.name.to_string()
+                            ),
+                            *c,
+                            "location of previous definition".to_string(),
+                        ));
+                    }
+                    ast::Symbol::Enum(c, _) => {
+                        self.diagnostics.push(Output::warning_with_note(
+                            id.loc,
+                            format!("{} is already defined as an enum", id.name.to_string()),
+                            *c,
+                            "location of previous definition".to_string(),
+                        ));
+                    }
+                    ast::Symbol::Struct(c, _) => {
+                        self.diagnostics.push(Output::warning_with_note(
+                            id.loc,
+                            format!("{} is already defined as a struct", id.name.to_string()),
+                            *c,
+                            "location of previous definition".to_string(),
+                        ));
+                    }
+                    ast::Symbol::Variable(c, _) => {
+                        self.diagnostics.push(Output::warning_with_note(
+                            id.loc,
+                            format!(
+                                "{} is already defined as a contract variable",
+                                id.name.to_string()
+                            ),
+                            *c,
+                            "location of previous definition".to_string(),
+                        ));
+                    }
+                    ast::Symbol::Function(v) => {
+                        self.diagnostics.push(Output::warning_with_note(
+                            id.loc,
+                            format!("{} is already defined as a function", id.name.to_string()),
+                            v[0].0,
+                            "location of previous definition".to_string(),
+                        ));
+                    }
+                }
+            }
+        }
+
+        self.symbols
+            .insert((contract_no, id.name.to_string()), symbol);
+
+        true
+    }
+
+    pub fn resolve_enum(&self, contract_no: Option<usize>, id: &pt::Identifier) -> Option<usize> {
+        if let Some(ast::Symbol::Enum(_, n)) = self.symbols.get(&(contract_no, id.name.to_owned()))
+        {
+            return Some(*n);
+        }
+
+        if contract_no.is_some() {
+            if let Some(ast::Symbol::Enum(_, n)) = self.symbols.get(&(None, id.name.to_owned())) {
+                return Some(*n);
+            }
+        }
+
+        None
+    }
+
+    pub fn resolve_contract(&self, id: &pt::Identifier) -> Option<usize> {
+        if let Some(ast::Symbol::Contract(_, n)) = self.symbols.get(&(None, id.name.to_owned())) {
+            return Some(*n);
+        }
+
+        None
+    }
+
+    pub fn resolve_func(
+        &mut self,
+        contract_no: usize,
+        id: &pt::Identifier,
+    ) -> Result<Vec<(pt::Loc, usize)>, ()> {
+        match self.symbols.get(&(Some(contract_no), id.name.to_owned())) {
+            Some(ast::Symbol::Function(v)) => Ok(v.clone()),
+            _ => {
+                self.diagnostics.push(Output::error(
+                    id.loc,
+                    "unknown function or type".to_string(),
+                ));
+
+                Err(())
+            }
+        }
+    }
+
+    pub fn resolve_var(&mut self, contract_no: usize, id: &pt::Identifier) -> Result<usize, ()> {
+        let mut s = self.symbols.get(&(Some(contract_no), id.name.to_owned()));
+
+        if s.is_none() {
+            s = self.symbols.get(&(None, id.name.to_owned()));
+        }
+
+        match s {
+            None => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("`{}' is not declared", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Enum(_, _)) => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("`{}' is an enum", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Struct(_, _)) => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("`{}' is a struct", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Function(_)) => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("`{}' is a function", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Contract(_, _)) => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("`{}' is a contract", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Variable(_, n)) => Ok(*n),
+        }
+    }
+
+    pub fn check_shadowing(&mut self, contract_no: usize, id: &pt::Identifier) {
+        let mut s = self.symbols.get(&(Some(contract_no), id.name.to_owned()));
+
+        if s.is_none() {
+            s = self.symbols.get(&(None, id.name.to_owned()));
+        }
+
+        match s {
+            Some(ast::Symbol::Enum(loc, _)) => {
+                self.diagnostics.push(Output::warning_with_note(
+                    id.loc,
+                    format!("declaration of `{}' shadows enum definition", id.name),
+                    *loc,
+                    "previous definition of enum".to_string(),
+                ));
+            }
+            Some(ast::Symbol::Struct(loc, _)) => {
+                self.diagnostics.push(Output::warning_with_note(
+                    id.loc,
+                    format!("declaration of `{}' shadows struct definition", id.name),
+                    *loc,
+                    "previous definition of struct".to_string(),
+                ));
+            }
+            Some(ast::Symbol::Function(v)) => {
+                let notes = v
+                    .iter()
+                    .map(|(pos, _)| Note {
+                        pos: *pos,
+                        message: "previous declaration of function".to_owned(),
+                    })
+                    .collect();
+                self.diagnostics.push(Output::warning_with_notes(
+                    id.loc,
+                    format!("declaration of `{}' shadows function", id.name),
+                    notes,
+                ));
+            }
+            Some(ast::Symbol::Variable(loc, _)) => {
+                self.diagnostics.push(Output::warning_with_note(
+                    id.loc,
+                    format!("declaration of `{}' shadows state variable", id.name),
+                    *loc,
+                    "previous declaration of state variable".to_string(),
+                ));
+            }
+            Some(ast::Symbol::Contract(loc, _)) => {
+                self.diagnostics.push(Output::warning_with_note(
+                    id.loc,
+                    format!("declaration of `{}' shadows contract name", id.name),
+                    *loc,
+                    "previous declaration of contract name".to_string(),
+                ));
+            }
+            None => {}
+        }
+    }
+
+    /// Resolve the parsed data type. The type can be a primitive, enum and also an arrays.
+    /// The type for address payable is "address payble" used as a type, and "payable" when
+    /// casting. So, we need to know what we are resolving for.
+    pub fn resolve_type(
+        &mut self,
+        contract_no: Option<usize>,
+        casting: bool,
+        id: &pt::Expression,
+    ) -> Result<ast::Type, ()> {
+        fn resolve_dimensions(
+            ast_dimensions: &[Option<(pt::Loc, BigInt)>],
+            ns: &mut ast::Namespace,
+        ) -> Result<Vec<Option<BigInt>>, ()> {
+            let mut dimensions = Vec::new();
+
+            for d in ast_dimensions.iter().rev() {
+                if let Some((loc, n)) = d {
+                    if n.is_zero() {
+                        ns.diagnostics.push(Output::decl_error(
+                            *loc,
+                            "zero size array not permitted".to_string(),
+                        ));
+                        return Err(());
+                    } else if n.is_negative() {
+                        ns.diagnostics.push(Output::decl_error(
+                            *loc,
+                            "negative size of array declared".to_string(),
+                        ));
+                        return Err(());
+                    }
+                    dimensions.push(Some(n.clone()));
+                } else {
+                    dimensions.push(None);
+                }
+            }
+
+            Ok(dimensions)
+        }
+
+        let (contract_name, id, dimensions) = self.expr_to_type(contract_no, &id)?;
+
+        if let pt::Expression::Type(_, ty) = &id {
+            assert_eq!(contract_name, None);
+
+            let ty = match ty {
+                pt::Type::Mapping(_, k, v) => {
+                    let key = self.resolve_type(contract_no, false, k)?;
+                    let value = self.resolve_type(contract_no, false, v)?;
+
+                    match key {
+                        ast::Type::Mapping(_, _) => {
+                            self.diagnostics.push(Output::decl_error(
+                                k.loc(),
+                                "key of mapping cannot be another mapping type".to_string(),
+                            ));
+                            return Err(());
+                        }
+                        ast::Type::Struct(_) => {
+                            self.diagnostics.push(Output::decl_error(
+                                k.loc(),
+                                "key of mapping cannot be struct type".to_string(),
+                            ));
+                            return Err(());
+                        }
+                        ast::Type::Array(_, _) => {
+                            self.diagnostics.push(Output::decl_error(
+                                k.loc(),
+                                "key of mapping cannot be array type".to_string(),
+                            ));
+                            return Err(());
+                        }
+                        _ => ast::Type::Mapping(Box::new(key), Box::new(value)),
+                    }
+                }
+                pt::Type::Payable => {
+                    if !casting {
+                        self.diagnostics.push(Output::decl_error(
+                            id.loc(),
+                            "‘payable’ cannot be used for type declarations, only casting. use ‘address payable’"
+                                .to_string(),
+                        ));
+                        return Err(());
+                    } else {
+                        ast::Type::Address(true)
+                    }
+                }
+                _ => ast::Type::from(ty),
+            };
+
+            return if dimensions.is_empty() {
+                Ok(ty)
+            } else {
+                Ok(ast::Type::Array(
+                    Box::new(ty),
+                    resolve_dimensions(&dimensions, self)?,
+                ))
+            };
+        }
+
+        let id = match id {
+            pt::Expression::Variable(id) => id,
+            _ => unreachable!(),
+        };
+
+        let contract_no = if let Some(contract_name) = contract_name {
+            match self.symbols.get(&(None, contract_name.name)) {
+                None => {
+                    self.diagnostics.push(Output::decl_error(
+                        id.loc,
+                        format!("contract type ‘{}’ not found", id.name),
+                    ));
+                    return Err(());
+                }
+                Some(ast::Symbol::Contract(_, n)) => Some(*n),
+                Some(ast::Symbol::Function(_)) => {
+                    self.diagnostics.push(Output::decl_error(
+                        id.loc,
+                        format!("‘{}’ is a function", id.name),
+                    ));
+                    return Err(());
+                }
+                Some(ast::Symbol::Variable(_, _)) => {
+                    self.diagnostics.push(Output::decl_error(
+                        id.loc,
+                        format!("‘{}’ is a contract variable", id.name),
+                    ));
+                    return Err(());
+                }
+                Some(ast::Symbol::Struct(_, _)) => {
+                    self.diagnostics.push(Output::decl_error(
+                        id.loc,
+                        format!("‘{}’ is a struct", id.name),
+                    ));
+                    return Err(());
+                }
+                Some(ast::Symbol::Enum(_, _)) => {
+                    self.diagnostics.push(Output::decl_error(
+                        id.loc,
+                        format!("‘{}’ is an enum variable", id.name),
+                    ));
+                    return Err(());
+                }
+            }
+        } else {
+            contract_no
+        };
+
+        let mut s = self.symbols.get(&(contract_no, id.name.to_owned()));
+
+        // try global scope
+        if s.is_none() && contract_no.is_some() {
+            s = self.symbols.get(&(None, id.name.to_owned()));
+        }
+
+        match s {
+            None => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("type ‘{}’ not found", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Enum(_, n)) if dimensions.is_empty() => Ok(ast::Type::Enum(*n)),
+            Some(ast::Symbol::Enum(_, n)) => Ok(ast::Type::Array(
+                Box::new(ast::Type::Enum(*n)),
+                resolve_dimensions(&dimensions, self)?,
+            )),
+            Some(ast::Symbol::Struct(_, n)) if dimensions.is_empty() => Ok(ast::Type::Struct(*n)),
+            Some(ast::Symbol::Struct(_, n)) => Ok(ast::Type::Array(
+                Box::new(ast::Type::Struct(*n)),
+                resolve_dimensions(&dimensions, self)?,
+            )),
+            Some(ast::Symbol::Contract(_, n)) if dimensions.is_empty() => {
+                Ok(ast::Type::Contract(*n))
+            }
+            Some(ast::Symbol::Contract(_, n)) => Ok(ast::Type::Array(
+                Box::new(ast::Type::Contract(*n)),
+                resolve_dimensions(&dimensions, self)?,
+            )),
+            Some(ast::Symbol::Function(_)) => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("‘{}’ is a function", id.name),
+                ));
+                Err(())
+            }
+            Some(ast::Symbol::Variable(_, _)) => {
+                self.diagnostics.push(Output::decl_error(
+                    id.loc,
+                    format!("‘{}’ is a contract variable", id.name),
+                ));
+                Err(())
+            }
+        }
+    }
+
+    // An array type can look like foo[2], if foo is an enum type. The lalrpop parses
+    // this as an expression, so we need to convert it to Type and check there are
+    // no unexpected expressions types.
+    pub fn expr_to_type(
+        &mut self,
+        contract_no: Option<usize>,
+        expr: &pt::Expression,
+    ) -> Result<(Option<pt::Identifier>, pt::Expression, Vec<ArrayDimension>), ()> {
+        let mut expr = expr;
+        let mut dimensions = Vec::new();
+
+        loop {
+            expr = match expr {
+                pt::Expression::ArraySubscript(_, r, None) => {
+                    dimensions.push(None);
+
+                    r.as_ref()
+                }
+                pt::Expression::ArraySubscript(_, r, Some(index)) => {
+                    dimensions.push(self.resolve_array_dimension(contract_no, index)?);
+
+                    r.as_ref()
+                }
+                pt::Expression::Variable(_) | pt::Expression::Type(_, _) => {
+                    return Ok((None, expr.clone(), dimensions))
+                }
+                pt::Expression::MemberAccess(_, namespace, id) => {
+                    if let pt::Expression::Variable(namespace) = namespace.as_ref() {
+                        return Ok((
+                            Some(namespace.clone()),
+                            pt::Expression::Variable(id.clone()),
+                            dimensions,
+                        ));
+                    } else {
+                        self.diagnostics.push(Output::decl_error(
+                            namespace.loc(),
+                            "expression found where contract type expected".to_string(),
+                        ));
+                        return Err(());
+                    }
+                }
+                _ => {
+                    self.diagnostics.push(Output::decl_error(
+                        expr.loc(),
+                        "expression found where type expected".to_string(),
+                    ));
+                    return Err(());
+                }
+            }
+        }
+    }
+
+    /// Resolve an expression which defines the array length, e.g. 2**8 in "bool[2**8]"
+    pub fn resolve_array_dimension(
+        &mut self,
+        contract_no: Option<usize>,
+        expr: &pt::Expression,
+    ) -> Result<ArrayDimension, ()> {
+        let symtable = Symtable::new();
+
+        let size_expr = expression(&expr, contract_no, self, &symtable, true)?;
+        match size_expr.ty() {
+            ast::Type::Uint(_) | ast::Type::Int(_) => {}
+            _ => {
+                self.diagnostics.push(Output::decl_error(
+                    expr.loc(),
+                    "expression is not a number".to_string(),
+                ));
+                return Err(());
+            }
+        }
+
+        match eval_const_number(&size_expr, contract_no, self) {
+            Ok(n) => Ok(Some(n)),
+            Err(d) => {
+                self.diagnostics.push(d);
+
+                Err(())
+            }
+        }
+    }
+}
+
+impl ast::Contract {
+    pub fn new(name: &str) -> Self {
+        ast::Contract {
+            name: name.to_owned(),
+            doc: Vec::new(),
+            functions: Vec::new(),
+            variables: Vec::new(),
+            top_of_contract_storage: BigInt::zero(),
+            creates: Vec::new(),
+            initializer: ControlFlowGraph::new(),
+        }
+    }
+
+    /// Return the index of the fallback function, if any
+    pub fn fallback_function(&self) -> Option<usize> {
+        for (i, f) in self.functions.iter().enumerate() {
+            if f.ty == pt::FunctionTy::Fallback {
+                return Some(i);
+            }
+        }
+        None
+    }
+
+    /// Return the index of the receive function, if any
+    pub fn receive_function(&self) -> Option<usize> {
+        for (i, f) in self.functions.iter().enumerate() {
+            if f.ty == pt::FunctionTy::Receive {
+                return Some(i);
+            }
+        }
+        None
+    }
+
+    pub fn emit<'a>(
+        &'a self,
+        ns: &'a ast::Namespace,
+        context: &'a inkwell::context::Context,
+        filename: &'a str,
+        opt: OptimizationLevel,
+    ) -> emit::Contract {
+        emit::Contract::build(context, self, ns, filename, opt)
+    }
+
+    /// Print the entire contract; storage initializers, constructors and functions and their CFGs
+    pub fn print_to_string(&self, ns: &ast::Namespace) -> String {
+        let mut out = format!("#\n# Contract: {}\n#\n\n", self.name);
+
+        out += "# storage initializer\n";
+        out += &self.initializer.to_string(self, ns);
+
+        for func in self.functions.iter() {
+            out += &format!("\n# {} {}\n", func.ty, func.signature);
+
+            if let Some(ref cfg) = func.cfg {
+                out += &cfg.to_string(self, ns);
+            }
+        }
+
+        out
+    }
+}

+ 1190 - 0
src/sema/statements.rs

@@ -0,0 +1,1190 @@
+use super::ast::*;
+use super::expression::{
+    cast, constructor_named_args, expression, function_call_expr, named_function_call_expr, new,
+};
+use super::symtable::{LoopScopes, Symtable};
+use num_bigint::BigInt;
+use output::Output;
+use parser::pt;
+
+pub fn resolve_function_body(
+    ast_f: &pt::FunctionDefinition,
+    contract_no: usize,
+    function_no: usize,
+    ns: &mut Namespace,
+) -> Result<(), ()> {
+    let mut symtable = Symtable::new();
+    let mut loops = LoopScopes::new();
+    let mut res = Vec::new();
+
+    // first add function parameters
+    for (i, p) in ast_f.params.iter().enumerate() {
+        let p = p.1.as_ref().unwrap();
+        if let Some(ref name) = p.name {
+            if let Some(pos) = symtable.add(
+                name,
+                ns.contracts[contract_no].functions[function_no].params[i]
+                    .ty
+                    .clone(),
+                ns,
+            ) {
+                ns.check_shadowing(contract_no, name);
+
+                symtable.arguments.push(Some(pos));
+            }
+        } else {
+            symtable.arguments.push(None);
+        }
+    }
+
+    // a function with no return values does not need a return statement
+    let mut return_required = !ast_f.returns.is_empty();
+
+    // If any of the return values are named, then the return statement can be omitted at
+    // the end of the function, and return values may be omitted too. Create variables to
+    // store the return values
+    for (i, p) in ast_f.returns.iter().enumerate() {
+        let ret = &ns.contracts[contract_no].functions[function_no].returns[i];
+
+        if let Some(ref name) = p.1.as_ref().unwrap().name {
+            return_required = false;
+
+            if let Some(pos) = symtable.add(name, ret.ty.clone(), ns) {
+                ns.check_shadowing(contract_no, name);
+
+                symtable.returns.push(pos);
+            }
+        } else {
+            // anonymous return
+            let id = pt::Identifier {
+                loc: p.0,
+                name: "".to_owned(),
+            };
+
+            let pos = symtable.add(&id, ret.ty.clone(), ns).unwrap();
+
+            symtable.returns.push(pos);
+        }
+    }
+
+    let reachable = statement(
+        &ast_f.body,
+        &mut res,
+        contract_no,
+        function_no,
+        &mut symtable,
+        &mut loops,
+        ns,
+    )?;
+
+    // ensure we have a return instruction
+    if reachable {
+        if let Some(Statement::Return(_, _)) = res.last() {
+            // ok
+        } else if return_required {
+            ns.diagnostics.push(Output::error(
+                ast_f.body.loc(),
+                "missing return statement".to_string(),
+            ));
+            return Err(());
+        } else {
+            // add implicit return
+            statement(
+                &pt::Statement::Return(pt::Loc(0, 0), None),
+                &mut res,
+                contract_no,
+                function_no,
+                &mut symtable,
+                &mut loops,
+                ns,
+            )?;
+        }
+    }
+
+    ns.contracts[contract_no].functions[function_no].body = res;
+    std::mem::swap(
+        &mut ns.contracts[contract_no].functions[function_no].symtable,
+        &mut symtable,
+    );
+
+    Ok(())
+}
+
+/// Resolve a statement
+fn statement(
+    stmt: &pt::Statement,
+    res: &mut Vec<Statement>,
+    contract_no: usize,
+    function_no: usize,
+    symtable: &mut Symtable,
+    loops: &mut LoopScopes,
+    ns: &mut Namespace,
+) -> Result<bool, ()> {
+    match stmt {
+        pt::Statement::VariableDefinition(loc, decl, initializer) => {
+            let var_ty = resolve_var_decl_ty(&decl.ty, &decl.storage, contract_no, ns)?;
+
+            let initializer = if let Some(init) = initializer {
+                let expr = expression(init, Some(contract_no), ns, symtable, false)?;
+
+                Some(cast(&decl.name.loc, expr, &var_ty, true, ns)?)
+            } else {
+                None
+            };
+
+            if let Some(pos) = symtable.add(&decl.name, var_ty.clone(), ns) {
+                ns.check_shadowing(contract_no, &decl.name);
+
+                res.push(Statement::VariableDecl(
+                    *loc,
+                    pos,
+                    Parameter {
+                        loc: decl.loc,
+                        ty: var_ty,
+                        name: decl.name.name.to_owned(),
+                    },
+                    initializer,
+                ));
+            }
+
+            Ok(true)
+        }
+        pt::Statement::Block(_, stmts) => {
+            symtable.new_scope();
+            let mut reachable = true;
+
+            for stmt in stmts {
+                if !reachable {
+                    ns.diagnostics.push(Output::error(
+                        stmt.loc(),
+                        "unreachable statement".to_string(),
+                    ));
+                    return Err(());
+                }
+                reachable = statement(&stmt, res, contract_no, function_no, symtable, loops, ns)?;
+            }
+
+            symtable.leave_scope();
+
+            Ok(reachable)
+        }
+        pt::Statement::Break(loc) => {
+            if loops.do_break() {
+                res.push(Statement::Break(*loc));
+                Ok(false)
+            } else {
+                ns.diagnostics.push(Output::error(
+                    stmt.loc(),
+                    "break statement not in loop".to_string(),
+                ));
+                Err(())
+            }
+        }
+        pt::Statement::Continue(loc) => {
+            if loops.do_continue() {
+                res.push(Statement::Continue(*loc));
+                Ok(false)
+            } else {
+                ns.diagnostics.push(Output::error(
+                    stmt.loc(),
+                    "continue statement not in loop".to_string(),
+                ));
+                Err(())
+            }
+        }
+        pt::Statement::While(loc, cond_expr, body) => {
+            let expr = expression(cond_expr, Some(contract_no), ns, symtable, false)?;
+
+            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns)?;
+
+            symtable.new_scope();
+            let mut body_stmts = Vec::new();
+            loops.new_scope();
+            statement(
+                body,
+                &mut body_stmts,
+                contract_no,
+                function_no,
+                symtable,
+                loops,
+                ns,
+            )?;
+            symtable.leave_scope();
+            loops.leave_scope();
+
+            res.push(Statement::While(*loc, true, cond, body_stmts));
+
+            Ok(true)
+        }
+        pt::Statement::DoWhile(loc, body, cond_expr) => {
+            let expr = expression(cond_expr, Some(contract_no), ns, symtable, false)?;
+
+            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns)?;
+
+            symtable.new_scope();
+            let mut body_stmts = Vec::new();
+            loops.new_scope();
+            statement(
+                body,
+                &mut body_stmts,
+                contract_no,
+                function_no,
+                symtable,
+                loops,
+                ns,
+            )?;
+            symtable.leave_scope();
+            loops.leave_scope();
+
+            res.push(Statement::DoWhile(*loc, true, body_stmts, cond));
+            Ok(true)
+        }
+        pt::Statement::If(loc, cond_expr, then, else_) => {
+            let expr = expression(cond_expr, Some(contract_no), ns, symtable, false)?;
+
+            let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns)?;
+
+            symtable.new_scope();
+            let mut then_stmts = Vec::new();
+            let mut reachable = statement(
+                then,
+                &mut then_stmts,
+                contract_no,
+                function_no,
+                symtable,
+                loops,
+                ns,
+            )?;
+            symtable.leave_scope();
+
+            let mut else_stmts = Vec::new();
+            if let Some(stmts) = else_ {
+                symtable.new_scope();
+                reachable &= statement(
+                    stmts,
+                    &mut else_stmts,
+                    contract_no,
+                    function_no,
+                    symtable,
+                    loops,
+                    ns,
+                )?;
+
+                symtable.leave_scope();
+            } else {
+                reachable = true;
+            }
+
+            res.push(Statement::If(*loc, reachable, cond, then_stmts, else_stmts));
+
+            Ok(reachable)
+        }
+        pt::Statement::Args(loc, _) => {
+            ns.diagnostics.push(Output::error(
+                *loc,
+                "expected code block, not list of named arguments".to_string(),
+            ));
+            Err(())
+        }
+        pt::Statement::For(loc, init_stmt, None, next_stmt, body_stmt) => {
+            symtable.new_scope();
+
+            let mut init = Vec::new();
+
+            if let Some(init_stmt) = init_stmt {
+                statement(
+                    init_stmt,
+                    &mut init,
+                    contract_no,
+                    function_no,
+                    symtable,
+                    loops,
+                    ns,
+                )?;
+            }
+
+            loops.new_scope();
+
+            let mut body = Vec::new();
+
+            if let Some(body_stmt) = body_stmt {
+                statement(
+                    body_stmt,
+                    &mut body,
+                    contract_no,
+                    function_no,
+                    symtable,
+                    loops,
+                    ns,
+                )?;
+            }
+
+            let control = loops.leave_scope();
+            let reachable = control.no_breaks > 0;
+            let mut next = Vec::new();
+
+            if let Some(next_stmt) = next_stmt {
+                statement(
+                    next_stmt,
+                    &mut next,
+                    contract_no,
+                    function_no,
+                    symtable,
+                    loops,
+                    ns,
+                )?;
+            }
+
+            symtable.leave_scope();
+
+            res.push(Statement::For {
+                loc: *loc,
+                reachable,
+                init,
+                next,
+                cond: None,
+                body,
+            });
+
+            Ok(reachable)
+        }
+        pt::Statement::For(loc, init_stmt, Some(cond_expr), next_stmt, body_stmt) => {
+            symtable.new_scope();
+
+            let mut init = Vec::new();
+            let mut body = Vec::new();
+            let mut next = Vec::new();
+
+            if let Some(init_stmt) = init_stmt {
+                statement(
+                    init_stmt,
+                    &mut init,
+                    contract_no,
+                    function_no,
+                    symtable,
+                    loops,
+                    ns,
+                )?;
+            }
+
+            let expr = expression(cond_expr, Some(contract_no), ns, symtable, false)?;
+
+            let cond = cast(&cond_expr.loc(), expr, &Type::Bool, true, ns)?;
+
+            // continue goes to next, and if that does exist, cond
+            loops.new_scope();
+
+            let mut body_reachable = match body_stmt {
+                Some(body_stmt) => statement(
+                    body_stmt,
+                    &mut body,
+                    contract_no,
+                    function_no,
+                    symtable,
+                    loops,
+                    ns,
+                )?,
+                None => true,
+            };
+
+            let control = loops.leave_scope();
+
+            if control.no_continues > 0 {
+                body_reachable = true;
+            }
+
+            if body_reachable {
+                if let Some(next_stmt) = next_stmt {
+                    statement(
+                        next_stmt,
+                        &mut next,
+                        contract_no,
+                        function_no,
+                        symtable,
+                        loops,
+                        ns,
+                    )?;
+                }
+            }
+
+            symtable.leave_scope();
+
+            res.push(Statement::For {
+                loc: *loc,
+                reachable: true,
+                init,
+                next,
+                cond: Some(cond),
+                body,
+            });
+
+            Ok(true)
+        }
+        pt::Statement::Return(loc, None) => {
+            let no_returns = ns.contracts[contract_no].functions[function_no]
+                .returns
+                .len();
+
+            if symtable.returns.len() != no_returns {
+                ns.diagnostics.push(Output::error(
+                    *loc,
+                    format!(
+                        "missing return value, {} return values expected",
+                        no_returns
+                    ),
+                ));
+                return Err(());
+            }
+
+            res.push(Statement::Return(
+                *loc,
+                symtable
+                    .returns
+                    .iter()
+                    .map(|pos| {
+                        Expression::Variable(pt::Loc(0, 0), symtable.vars[*pos].ty.clone(), *pos)
+                    })
+                    .collect(),
+            ));
+
+            Ok(false)
+        }
+        pt::Statement::Return(loc, Some(returns)) => {
+            let vals = return_with_values(returns, loc, contract_no, function_no, symtable, ns)?;
+
+            res.push(Statement::Return(*loc, vals));
+
+            Ok(false)
+        }
+        pt::Statement::Expression(loc, expr) => {
+            // delete statement
+            if let pt::Expression::Delete(_, expr) = expr {
+                let expr = expression(expr, Some(contract_no), ns, symtable, false)?;
+
+                return if let Type::StorageRef(ty) = expr.ty() {
+                    if expr.ty().is_mapping() {
+                        ns.diagnostics.push(Output::error(
+                            *loc,
+                            "‘delete’ cannot be applied to mapping type".to_string(),
+                        ));
+                        return Err(());
+                    }
+
+                    res.push(Statement::Delete(*loc, ty.as_ref().clone(), expr));
+
+                    Ok(true)
+                } else {
+                    ns.diagnostics.push(Output::error(
+                        *loc,
+                        "argument to ‘delete’ should be storage reference".to_string(),
+                    ));
+
+                    Err(())
+                };
+            }
+
+            // is it a destructure statement
+            if let pt::Expression::Assign(_, var, expr) = expr {
+                if let pt::Expression::List(_, var) = var.as_ref() {
+                    res.push(destructure(loc, var, expr, contract_no, symtable, ns)?);
+
+                    // if a noreturn function was called, then the destructure would not resolve
+                    return Ok(true);
+                }
+            }
+
+            // the rest
+            let expr = expression(expr, Some(contract_no), ns, symtable, false)?;
+
+            let reachable = expr.ty() != Type::Unreachable;
+
+            res.push(Statement::Expression(*loc, reachable, expr));
+
+            Ok(reachable)
+        }
+        pt::Statement::Try(loc, expr, returns_and_ok, error_stmt, catch_stmt) => {
+            let (stmt, reachable) = try_catch(
+                loc,
+                expr,
+                returns_and_ok,
+                error_stmt,
+                catch_stmt,
+                contract_no,
+                function_no,
+                symtable,
+                loops,
+                ns,
+            )?;
+            res.push(stmt);
+
+            Ok(reachable)
+        }
+        _ => unreachable!(),
+    }
+}
+
+/// Resolve destructuring assignment
+fn destructure(
+    loc: &pt::Loc,
+    vars: &[(pt::Loc, Option<pt::Parameter>)],
+    expr: &pt::Expression,
+    contract_no: usize,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<Statement, ()> {
+    let expr = match expr {
+        pt::Expression::FunctionCall(loc, ty, args) => {
+            function_call_expr(loc, ty, args, Some(contract_no), ns, symtable)?
+        }
+        pt::Expression::NamedFunctionCall(loc, ty, args) => {
+            named_function_call_expr(loc, ty, args, Some(contract_no), ns, symtable)?
+        }
+        _ => {
+            let mut list = Vec::new();
+
+            for e in parameter_list_to_expr_list(expr, ns)? {
+                let e = expression(e, Some(contract_no), ns, symtable, false)?;
+
+                match e.ty() {
+                    Type::Void | Type::Unreachable => {
+                        ns.diagnostics.push(Output::error(
+                            e.loc(),
+                            "function does not return a value".to_string(),
+                        ));
+                        return Err(());
+                    }
+                    _ => (),
+                }
+
+                list.push(e);
+            }
+
+            Expression::List(*loc, list)
+        }
+    };
+
+    let mut tys = expr.tys();
+
+    // Return type void or unreachable are synthetic
+    if tys.len() == 1 && (tys[0] == Type::Unreachable || tys[0] == Type::Void) {
+        tys.truncate(0);
+    }
+
+    if vars.len() != tys.len() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "destructuring assignment has {} elements on the left and {} on the right",
+                vars.len(),
+                tys.len()
+            ),
+        ));
+        return Err(());
+    }
+
+    // first resolve the fields
+    let mut fields = Vec::new();
+
+    for (i, e) in vars.iter().enumerate() {
+        match &e.1 {
+            None => {
+                fields.push(DestructureField::None);
+            }
+            Some(pt::Parameter {
+                loc,
+                ty,
+                storage,
+                name: None,
+            }) => {
+                if let Some(storage) = storage {
+                    ns.diagnostics.push(Output::error(
+                        *storage.loc(),
+                        format!("storage modifier ‘{}’ not permitted on assignment", storage),
+                    ));
+                    return Err(());
+                }
+
+                // ty will just be a normal expression, not a type
+                let e = expression(ty, Some(contract_no), ns, symtable, false)?;
+
+                match &e {
+                    Expression::ConstantVariable(_, _, n) => {
+                        ns.diagnostics.push(Output::error(
+                            *loc,
+                            format!(
+                                "cannot assign to constant ‘{}’",
+                                ns.contracts[contract_no].variables[*n].name
+                            ),
+                        ));
+                        return Err(());
+                    }
+                    Expression::StorageVariable(_, _, _) | Expression::Variable(_, _, _) => (),
+                    _ => match e.ty() {
+                        Type::Ref(_) | Type::StorageRef(_) => (),
+                        _ => {
+                            ns.diagnostics.push(Output::error(
+                                *loc,
+                                "expression is not assignable".to_string(),
+                            ));
+                            return Err(());
+                        }
+                    },
+                }
+
+                // here we only CHECK if we can cast the type
+                let _ = cast(
+                    &loc,
+                    Expression::FunctionArg(*loc, tys[i].clone(), i),
+                    e.ty().deref_any(),
+                    true,
+                    ns,
+                )?;
+
+                fields.push(DestructureField::Expression(e));
+            }
+            Some(pt::Parameter {
+                loc,
+                ty,
+                storage,
+                name: Some(name),
+            }) => {
+                let ty = resolve_var_decl_ty(&ty, &storage, contract_no, ns)?;
+
+                // here we only CHECK if we can cast the type
+                let _ = cast(
+                    loc,
+                    Expression::FunctionArg(e.0, tys[i].clone(), i),
+                    ty.deref_any(),
+                    true,
+                    ns,
+                )?;
+
+                if let Some(pos) = symtable.add(&name, ty.clone(), ns) {
+                    ns.check_shadowing(contract_no, &name);
+
+                    fields.push(DestructureField::VariableDecl(
+                        pos,
+                        Parameter {
+                            loc: *loc,
+                            name: name.name.to_owned(),
+                            ty,
+                        },
+                    ));
+                }
+            }
+        }
+    }
+
+    Ok(Statement::Destructure(*loc, fields, expr))
+}
+
+/// Resolve the type of a variable declaration
+fn resolve_var_decl_ty(
+    ty: &pt::Expression,
+    storage: &Option<pt::StorageLocation>,
+    contract_no: usize,
+    ns: &mut Namespace,
+) -> Result<Type, ()> {
+    let mut var_ty = ns.resolve_type(Some(contract_no), false, &ty)?;
+
+    if let Some(storage) = storage {
+        if !var_ty.can_have_data_location() {
+            ns.diagnostics.push(Output::error(
+                *storage.loc(),
+                format!(
+                    "data location ‘{}’ only allowed for array, struct or mapping type",
+                    storage
+                ),
+            ));
+            return Err(());
+        }
+
+        if let pt::StorageLocation::Storage(_) = storage {
+            var_ty = Type::StorageRef(Box::new(var_ty));
+        }
+
+        // Note we are completely ignoring memory or calldata data locations. Everything
+        // will be stored in memory.
+    }
+
+    if var_ty.contains_mapping(ns) && !var_ty.is_contract_storage() {
+        ns.diagnostics.push(Output::error(
+            ty.loc(),
+            "mapping only allowed in storage".to_string(),
+        ));
+        return Err(());
+    }
+
+    if !var_ty.is_contract_storage() && var_ty.size_hint(ns) > BigInt::from(1024 * 1024) {
+        ns.diagnostics.push(Output::error(
+            ty.loc(),
+            "type to large to fit into memory".to_string(),
+        ));
+        return Err(());
+    }
+
+    Ok(var_ty)
+}
+
+/// Parse return statement with values
+fn return_with_values(
+    returns: &pt::Expression,
+    loc: &pt::Loc,
+    contract_no: usize,
+    function_no: usize,
+    symtable: &mut Symtable,
+    ns: &mut Namespace,
+) -> Result<Vec<Expression>, ()> {
+    let returns = parameter_list_to_expr_list(returns, ns)?;
+
+    let no_returns = ns.contracts[contract_no].functions[function_no]
+        .returns
+        .len();
+
+    if no_returns > 0 && returns.is_empty() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "missing return value, {} return values expected",
+                no_returns
+            ),
+        ));
+        return Err(());
+    }
+
+    if no_returns == 0 && !returns.is_empty() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            "function has no return values".to_string(),
+        ));
+        return Err(());
+    }
+
+    if no_returns != returns.len() {
+        ns.diagnostics.push(Output::error(
+            *loc,
+            format!(
+                "incorrect number of return values, expected {} but got {}",
+                no_returns,
+                returns.len()
+            ),
+        ));
+        return Err(());
+    }
+
+    let mut exprs = Vec::new();
+
+    for (i, r) in returns.iter().enumerate() {
+        let e = expression(r, Some(contract_no), ns, symtable, false)?;
+
+        exprs.push(cast(
+            &r.loc(),
+            e,
+            &ns.contracts[contract_no].functions[function_no].returns[i]
+                .ty
+                .clone(),
+            true,
+            ns,
+        )?);
+    }
+
+    Ok(exprs)
+}
+
+/// The parser generates parameter lists for lists. Sometimes this needs to be a
+/// simple expression list.
+pub fn parameter_list_to_expr_list<'a>(
+    e: &'a pt::Expression,
+    ns: &mut Namespace,
+) -> Result<Vec<&'a pt::Expression>, ()> {
+    if let pt::Expression::List(_, v) = &e {
+        let mut list = Vec::new();
+        let mut broken = false;
+
+        for e in v {
+            match &e.1 {
+                None => {
+                    ns.diagnostics
+                        .push(Output::error(e.0, "stray comma".to_string()));
+                    broken = true;
+                }
+                Some(pt::Parameter {
+                    name: Some(name), ..
+                }) => {
+                    ns.diagnostics
+                        .push(Output::error(name.loc, "single value expected".to_string()));
+                    broken = true;
+                }
+                Some(pt::Parameter {
+                    storage: Some(storage),
+                    ..
+                }) => {
+                    ns.diagnostics.push(Output::error(
+                        *storage.loc(),
+                        "storage specified not permitted here".to_string(),
+                    ));
+                    broken = true;
+                }
+                Some(pt::Parameter { ty, .. }) => {
+                    list.push(ty);
+                }
+            }
+        }
+
+        if !broken {
+            Ok(list)
+        } else {
+            Err(())
+        }
+    } else {
+        Ok(vec![e])
+    }
+}
+
+/// Parse try catch
+#[allow(clippy::type_complexity)]
+fn try_catch(
+    loc: &pt::Loc,
+    expr: &pt::Expression,
+    returns_and_ok: &Option<(Vec<(pt::Loc, Option<pt::Parameter>)>, Box<pt::Statement>)>,
+    error_stmt: &Option<Box<(pt::Identifier, pt::Parameter, pt::Statement)>>,
+    catch_stmt: &(pt::Parameter, pt::Statement),
+    contract_no: usize,
+    function_no: usize,
+    symtable: &mut Symtable,
+    loops: &mut LoopScopes,
+    ns: &mut Namespace,
+) -> Result<(Statement, bool), ()> {
+    let mut expr = expr;
+    let mut ok = None;
+
+    while let pt::Expression::FunctionCallBlock(_, e, block) = expr {
+        if ok.is_some() {
+            ns.diagnostics.push(Output::error(
+                block.loc(),
+                "unexpected code block".to_string(),
+            ));
+            return Err(());
+        }
+
+        ok = Some(block.as_ref());
+
+        expr = e.as_ref();
+    }
+
+    let fcall = match expr {
+        pt::Expression::FunctionCall(loc, ty, args) => {
+            function_call_expr(loc, ty, args, Some(contract_no), ns, symtable)?
+        }
+        pt::Expression::NamedFunctionCall(loc, ty, args) => {
+            named_function_call_expr(loc, ty, args, Some(contract_no), ns, symtable)?
+        }
+        pt::Expression::New(loc, call) => {
+            let mut call = call.as_ref();
+
+            while let pt::Expression::FunctionCallBlock(_, expr, block) = call {
+                if ok.is_some() {
+                    ns.diagnostics.push(Output::error(
+                        block.loc(),
+                        "unexpected code block".to_string(),
+                    ));
+                    return Err(());
+                }
+
+                ok = Some(block.as_ref());
+
+                call = expr.as_ref();
+            }
+
+            match call {
+                pt::Expression::FunctionCall(_, ty, args) => {
+                    new(loc, ty, args, Some(contract_no), ns, symtable)?
+                }
+                pt::Expression::NamedFunctionCall(_, ty, args) => {
+                    constructor_named_args(loc, ty, args, Some(contract_no), ns, symtable)?
+                }
+                _ => unreachable!(),
+            }
+        }
+        _ => {
+            ns.diagnostics.push(Output::error(
+                expr.loc(),
+                "try only supports external calls or constructor calls".to_string(),
+            ));
+            return Err(());
+        }
+    };
+
+    let mut returns = &Vec::new();
+
+    if let Some((rets, block)) = returns_and_ok {
+        if ok.is_some() {
+            ns.diagnostics.push(Output::error(
+                block.loc(),
+                "unexpected code block".to_string(),
+            ));
+            return Err(());
+        }
+
+        ok = Some(block);
+
+        returns = rets;
+    }
+
+    let ok = match ok {
+        Some(ok) => ok,
+        None => {
+            // position after the expression
+            let pos = expr.loc().1;
+
+            ns.diagnostics.push(Output::error(
+                pt::Loc(pos, pos),
+                "code block missing for no catch".to_string(),
+            ));
+            return Err(());
+        }
+    };
+
+    symtable.new_scope();
+
+    let mut args = match &fcall {
+        Expression::ExternalFunctionCall {
+            contract_no,
+            function_no,
+            ..
+        } => {
+            let ftype = &ns.contracts[*contract_no].functions[*function_no];
+
+            if returns.len() != ftype.returns.len() {
+                ns.diagnostics.push(Output::error(
+                    expr.loc(),
+                    format!(
+                        "try returns list has {} entries while function returns {} values",
+                        ftype.returns.len(),
+                        returns.len()
+                    ),
+                ));
+                return Err(());
+            }
+
+            ftype.returns.iter().map(|ret| ret.ty.clone()).collect()
+        }
+        Expression::Constructor { contract_no, .. } => match returns.len() {
+            0 => Vec::new(),
+            1 => vec![Type::Contract(*contract_no)],
+            _ => {
+                ns.diagnostics.push(Output::error(
+                    expr.loc(),
+                    format!(
+                        "constructor returns single contract, not {} values",
+                        returns.len()
+                    ),
+                ));
+                return Err(());
+            }
+        },
+        _ => {
+            ns.diagnostics.push(Output::error(
+                expr.loc(),
+                "try only supports external calls or constructor calls".to_string(),
+            ));
+            return Err(());
+        }
+    };
+
+    symtable.new_scope();
+
+    let mut params = Vec::new();
+    let mut broken = false;
+    for param in returns {
+        let arg_ty = args.remove(0);
+
+        match &param.1 {
+            Some(pt::Parameter {
+                ty, storage, name, ..
+            }) => {
+                let ret_ty = resolve_var_decl_ty(&ty, &storage, contract_no, ns)?;
+
+                if arg_ty != ret_ty {
+                    ns.diagnostics.push(Output::error(
+                        ty.loc(),
+                        format!(
+                            "type ‘{}’ does not match return value of function ‘{}’",
+                            ret_ty.to_string(ns),
+                            arg_ty.to_string(ns)
+                        ),
+                    ));
+                    broken = true;
+                }
+
+                if let Some(name) = name {
+                    if let Some(pos) = symtable.add(&name, ret_ty.clone(), ns) {
+                        ns.check_shadowing(contract_no, &name);
+                        params.push((
+                            Some(pos),
+                            Parameter {
+                                loc: param.0,
+                                ty: ret_ty,
+                                name: name.name.to_string(),
+                            },
+                        ));
+                    }
+                } else {
+                    params.push((
+                        None,
+                        Parameter {
+                            loc: param.0,
+                            ty: ret_ty,
+                            name: "".to_string(),
+                        },
+                    ));
+                }
+            }
+            None => {
+                ns.diagnostics
+                    .push(Output::error(param.0, "missing return type".to_string()));
+                broken = true;
+            }
+        }
+    }
+
+    if broken {
+        return Err(());
+    }
+
+    let mut ok_resolved = Vec::new();
+
+    let mut finally_reachable = statement(
+        &ok,
+        &mut ok_resolved,
+        contract_no,
+        function_no,
+        symtable,
+        loops,
+        ns,
+    )?;
+
+    symtable.leave_scope();
+
+    let error_resolved = if let Some(error_stmt) = error_stmt {
+        if error_stmt.0.name != "Error" {
+            ns.diagnostics.push(Output::error(
+                error_stmt.0.loc,
+                format!(
+                    "only catch ‘Error’ is supported, not ‘{}’",
+                    error_stmt.0.name
+                ),
+            ));
+            return Err(());
+        }
+
+        let error_ty =
+            resolve_var_decl_ty(&error_stmt.1.ty, &error_stmt.1.storage, contract_no, ns)?;
+
+        if error_ty != Type::String {
+            ns.diagnostics.push(Output::error(
+                error_stmt.1.ty.loc(),
+                format!(
+                    "catch Error(...) can only take ‘string memory’, not ‘{}’",
+                    error_ty.to_string(ns)
+                ),
+            ));
+        }
+
+        symtable.new_scope();
+
+        let mut error_pos = None;
+        let mut error_stmt_resolved = Vec::new();
+        let mut error_param = Parameter {
+            loc: error_stmt.0.loc,
+            ty: Type::String,
+            name: "".to_string(),
+        };
+
+        if let Some(name) = &error_stmt.1.name {
+            if let Some(pos) = symtable.add(&name, Type::String, ns) {
+                ns.check_shadowing(contract_no, &name);
+
+                error_pos = Some(pos);
+                error_param.name = name.name.to_string();
+            }
+        }
+
+        let reachable = statement(
+            &error_stmt.2,
+            &mut error_stmt_resolved,
+            contract_no,
+            function_no,
+            symtable,
+            loops,
+            ns,
+        )?;
+
+        finally_reachable &= reachable;
+
+        symtable.leave_scope();
+
+        Some((error_pos, error_param, error_stmt_resolved))
+    } else {
+        None
+    };
+
+    let catch_ty = resolve_var_decl_ty(&catch_stmt.0.ty, &catch_stmt.0.storage, contract_no, ns)?;
+
+    if catch_ty != Type::DynamicBytes {
+        ns.diagnostics.push(Output::error(
+            catch_stmt.0.ty.loc(),
+            format!(
+                "catch can only take ‘bytes memory’, not ‘{}’",
+                catch_ty.to_string(ns)
+            ),
+        ));
+        return Err(());
+    }
+
+    symtable.new_scope();
+
+    let mut catch_param = Parameter {
+        loc: catch_stmt.0.loc,
+        ty: Type::DynamicBytes,
+        name: "".to_owned(),
+    };
+    let mut catch_param_pos = None;
+    let mut catch_stmt_resolved = Vec::new();
+
+    if let Some(name) = &catch_stmt.0.name {
+        if let Some(pos) = symtable.add(&name, catch_ty, ns) {
+            ns.check_shadowing(contract_no, &name);
+            catch_param_pos = Some(pos);
+            catch_param.name = name.name.to_string();
+        }
+    }
+
+    let reachable = statement(
+        &catch_stmt.1,
+        &mut catch_stmt_resolved,
+        contract_no,
+        function_no,
+        symtable,
+        loops,
+        ns,
+    )?;
+
+    finally_reachable &= reachable;
+
+    symtable.leave_scope();
+
+    let stmt = Statement::TryCatch {
+        loc: *loc,
+        expr: fcall,
+        reachable: finally_reachable,
+        returns: params,
+        error: error_resolved,
+        ok_stmt: ok_resolved,
+        catch_param,
+        catch_param_pos,
+        catch_stmt: catch_stmt_resolved,
+    };
+
+    Ok((stmt, finally_reachable))
+}

+ 0 - 0
src/resolver/structs.rs → src/sema/structs.rs


+ 135 - 0
src/sema/symtable.rs

@@ -0,0 +1,135 @@
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::LinkedList;
+use std::str;
+
+use output::Output;
+use parser::pt;
+use sema::ast::{Namespace, Type};
+
+#[derive(Clone)]
+pub struct Variable {
+    pub id: pt::Identifier,
+    pub ty: Type,
+    pub pos: usize,
+}
+
+struct VarScope(HashMap<String, usize>, Option<HashSet<usize>>);
+
+#[derive(Default)]
+pub struct Symtable {
+    pub vars: Vec<Variable>,
+    names: LinkedList<VarScope>,
+    pub arguments: Vec<Option<usize>>,
+    pub returns: Vec<usize>,
+}
+
+impl Symtable {
+    pub fn new() -> Self {
+        let mut list = LinkedList::new();
+        list.push_front(VarScope(HashMap::new(), None));
+        Symtable {
+            vars: Vec::new(),
+            names: list,
+            arguments: Vec::new(),
+            returns: Vec::new(),
+        }
+    }
+
+    pub fn add(&mut self, id: &pt::Identifier, ty: Type, ns: &mut Namespace) -> Option<usize> {
+        let pos = self.vars.len();
+
+        self.vars.push(Variable {
+            id: id.clone(),
+            ty,
+            pos,
+        });
+
+        // the variable has no name, like unnamed return or parameters values
+        if !id.name.is_empty() {
+            if let Some(ref prev) = self.find(&id.name) {
+                ns.diagnostics.push(Output::error_with_note(
+                    id.loc,
+                    format!("{} is already declared", id.name.to_string()),
+                    prev.id.loc,
+                    "location of previous declaration".to_string(),
+                ));
+                return None;
+            }
+
+            self.names
+                .front_mut()
+                .unwrap()
+                .0
+                .insert(id.name.to_string(), pos);
+        }
+
+        Some(pos)
+    }
+
+    pub fn find(&self, name: &str) -> Option<&Variable> {
+        for scope in &self.names {
+            if let Some(n) = scope.0.get(name) {
+                return Some(&self.vars[*n]);
+            }
+        }
+
+        None
+    }
+
+    pub fn new_scope(&mut self) {
+        self.names.push_front(VarScope(HashMap::new(), None));
+    }
+
+    pub fn leave_scope(&mut self) {
+        self.names.pop_front();
+    }
+
+    pub fn get_name(&self, pos: usize) -> &str {
+        &self.vars[pos].id.name
+    }
+}
+
+pub struct LoopScope {
+    pub no_breaks: usize,
+    pub no_continues: usize,
+}
+
+pub struct LoopScopes(LinkedList<LoopScope>);
+
+impl LoopScopes {
+    pub fn new() -> Self {
+        LoopScopes(LinkedList::new())
+    }
+
+    pub fn new_scope(&mut self) {
+        self.0.push_front(LoopScope {
+            no_breaks: 0,
+            no_continues: 0,
+        })
+    }
+
+    pub fn leave_scope(&mut self) -> LoopScope {
+        self.0.pop_front().unwrap()
+    }
+
+    pub fn do_break(&mut self) -> bool {
+        match self.0.front_mut() {
+            Some(scope) => {
+                scope.no_breaks += 1;
+                true
+            }
+            None => false,
+        }
+    }
+
+    pub fn do_continue(&mut self) -> bool {
+        match self.0.front_mut() {
+            Some(scope) => {
+                scope.no_continues += 1;
+                true
+            }
+            None => false,
+        }
+    }
+}

+ 27 - 52
src/resolver/types.rs → src/sema/types.rs

@@ -1,4 +1,4 @@
-use super::{Contract, EnumDecl, Namespace, StructDecl, StructField, Symbol, Type};
+use super::ast::{Contract, EnumDecl, Namespace, StructDecl, StructField, Symbol, Type};
 use output::Output;
 use parser::pt;
 use std::collections::HashMap;
@@ -6,8 +6,7 @@ use Target;
 
 /// Resolve all the types we can find (enums, structs, contracts). structs can have other
 /// structs as fields, including ones that have not been declared yet.
-pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
-    let mut errors = Vec::new();
+pub fn resolve(s: &pt::SourceUnit, target: Target) -> Namespace {
     let mut ns = Namespace::new(
         target,
         match target {
@@ -25,17 +24,17 @@ pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
         match part {
             pt::SourceUnitPart::PragmaDirective(name, value) => {
                 if name.name == "solidity" {
-                    errors.push(Output::info(
+                    ns.diagnostics.push(Output::info(
                         pt::Loc(name.loc.0, value.loc.1),
                         "pragma ‘solidity’ is ignored".to_string(),
                     ));
                 } else if name.name == "experimental" && value.string == "ABIEncoderV2" {
-                    errors.push(Output::info(
+                    ns.diagnostics.push(Output::info(
                         pt::Loc(name.loc.0, value.loc.1),
                         "pragma ‘experimental’ with value ‘ABIEncoderV2’ is ignored".to_string(),
                     ));
                 } else {
-                    errors.push(Output::warning(
+                    ns.diagnostics.push(Output::warning(
                         pt::Loc(name.loc.0, value.loc.1),
                         format!(
                             "unknown pragma ‘{}’ with value ‘{}’ ignored",
@@ -45,17 +44,16 @@ pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
                 }
             }
             pt::SourceUnitPart::ContractDefinition(def) => {
-                resolve_contract(&def, &mut structs, &mut errors, &mut ns);
+                resolve_contract(&def, &mut structs, &mut ns);
             }
             pt::SourceUnitPart::EnumDefinition(def) => {
-                let _ = enum_decl(&def, None, &mut ns, &mut errors);
+                let _ = enum_decl(&def, None, &mut ns);
             }
             pt::SourceUnitPart::StructDefinition(def) => {
                 if ns.add_symbol(
                     None,
                     &def.name,
                     Symbol::Struct(def.name.loc, ns.structs.len()),
-                    &mut errors,
                 ) {
                     let s = StructDecl {
                         name: def.name.name.to_owned(),
@@ -73,7 +71,7 @@ pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
 
     // now we can resolve the fields for the structs
     for (mut decl, def, contract) in structs {
-        if let Some(fields) = struct_decl(def, contract, &mut ns, &mut errors) {
+        if let Some(fields) = struct_decl(def, contract, &mut ns) {
             decl.fields = fields;
             ns.structs.push(decl);
         }
@@ -82,13 +80,8 @@ pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
     // struct can contain other structs, and we have to check for recursiveness,
     // i.e. "struct a { b f1; } struct b { a f1; }"
     for s in 0..ns.structs.len() {
-        fn check(
-            s: usize,
-            struct_fields: &mut Vec<usize>,
-            ns: &Namespace,
-            errors: &mut Vec<Output>,
-        ) {
-            let def = &ns.structs[s];
+        fn check(s: usize, struct_fields: &mut Vec<usize>, ns: &mut Namespace) {
+            let def = ns.structs[s].clone();
             let mut types_seen = Vec::new();
 
             for field in &def.fields {
@@ -100,7 +93,7 @@ pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
                     types_seen.push(n);
 
                     if struct_fields.contains(&n) {
-                        errors.push(Output::error_with_note(
+                        ns.diagnostics.push(Output::error_with_note(
                             def.loc,
                             format!("struct ‘{}’ has infinite size", def.name),
                             field.loc,
@@ -108,39 +101,33 @@ pub fn resolve(s: &pt::SourceUnit, target: Target) -> (Namespace, Vec<Output>) {
                         ));
                     } else {
                         struct_fields.push(n);
-                        check(n, struct_fields, ns, errors);
+                        check(n, struct_fields, ns);
                     }
                 }
             }
         };
 
-        check(s, &mut vec![s], &ns, &mut errors);
+        check(s, &mut vec![s], &mut ns);
     }
 
-    (ns, errors)
+    ns
 }
 
 /// Resolve all the types in a contract
 fn resolve_contract<'a>(
     def: &'a pt::ContractDefinition,
     structs: &mut Vec<(StructDecl, &'a pt::StructDefinition, Option<usize>)>,
-    errors: &mut Vec<Output>,
     ns: &mut Namespace,
 ) -> bool {
     let contract_no = ns.contracts.len();
     ns.contracts.push(Contract::new(&def.name.name));
 
-    let mut broken = !ns.add_symbol(
-        None,
-        &def.name,
-        Symbol::Contract(def.loc, contract_no),
-        errors,
-    );
+    let mut broken = !ns.add_symbol(None, &def.name, Symbol::Contract(def.loc, contract_no));
 
     for parts in &def.parts {
         match parts {
             pt::ContractPart::EnumDefinition(ref e) => {
-                if !enum_decl(e, Some(contract_no), ns, errors) {
+                if !enum_decl(e, Some(contract_no), ns) {
                     broken = true;
                 }
             }
@@ -149,7 +136,6 @@ fn resolve_contract<'a>(
                     Some(contract_no),
                     &s.name,
                     Symbol::Struct(s.name.loc, structs.len()),
-                    errors,
                 ) {
                     let decl = StructDecl {
                         name: s.name.name.to_owned(),
@@ -178,13 +164,12 @@ pub fn struct_decl(
     def: &pt::StructDefinition,
     contract_no: Option<usize>,
     ns: &mut Namespace,
-    errors: &mut Vec<Output>,
 ) -> Option<Vec<StructField>> {
     let mut valid = true;
     let mut fields: Vec<StructField> = Vec::new();
 
     for field in &def.fields {
-        let ty = match ns.resolve_type(contract_no, false, &field.ty, errors) {
+        let ty = match ns.resolve_type(contract_no, false, &field.ty) {
             Ok(s) => s,
             Err(()) => {
                 valid = false;
@@ -193,7 +178,7 @@ pub fn struct_decl(
         };
 
         if let Some(other) = fields.iter().find(|f| f.name == field.name.name) {
-            errors.push(Output::error_with_note(
+            ns.diagnostics.push(Output::error_with_note(
                 field.name.loc,
                 format!(
                     "struct ‘{}’ has duplicate struct field ‘{}’",
@@ -211,7 +196,7 @@ pub fn struct_decl(
         // in structs, but this is perfectly possible. The struct would not be
         // allowed as parameter/return types of public functions though.
         if let Some(storage) = &field.storage {
-            errors.push(Output::error(
+            ns.diagnostics.push(Output::error(
                 *storage.loc(),
                 format!(
                     "storage location ‘{}’ not allowed for struct field",
@@ -230,7 +215,7 @@ pub fn struct_decl(
 
     if fields.is_empty() {
         if valid {
-            errors.push(Output::error(
+            ns.diagnostics.push(Output::error(
                 def.name.loc,
                 format!("struct definition for ‘{}’ has no fields", def.name.name),
             ));
@@ -248,16 +233,11 @@ pub fn struct_decl(
 
 /// Parse enum declaration. If the declaration is invalid, it is still generated
 /// so that we can continue parsing, with errors recorded.
-fn enum_decl(
-    enum_: &pt::EnumDefinition,
-    contract_no: Option<usize>,
-    ns: &mut Namespace,
-    errors: &mut Vec<Output>,
-) -> bool {
+fn enum_decl(enum_: &pt::EnumDefinition, contract_no: Option<usize>, ns: &mut Namespace) -> bool {
     let mut valid = true;
 
     let mut bits = if enum_.values.is_empty() {
-        errors.push(Output::error(
+        ns.diagnostics.push(Output::error(
             enum_.name.loc,
             format!("enum ‘{}’ is missing fields", enum_.name.name),
         ));
@@ -282,7 +262,7 @@ fn enum_decl(
 
     for (i, e) in enum_.values.iter().enumerate() {
         if let Some(prev) = entries.get(&e.name.to_string()) {
-            errors.push(Output::error_with_note(
+            ns.diagnostics.push(Output::error_with_note(
                 e.loc,
                 format!("duplicate enum value {}", e.name),
                 prev.0,
@@ -309,12 +289,7 @@ fn enum_decl(
 
     ns.enums.push(decl);
 
-    if !ns.add_symbol(
-        contract_no,
-        &enum_.name,
-        Symbol::Enum(enum_.name.loc, pos),
-        errors,
-    ) {
+    if !ns.add_symbol(contract_no, &enum_.name, Symbol::Enum(enum_.name.loc, pos)) {
         valid = false;
     }
 
@@ -339,7 +314,7 @@ fn enum_256values_is_uint8() {
         name: "first".into(),
     });
 
-    assert!(enum_decl(&e, None, &mut ns, &mut Vec::new()));
+    assert!(enum_decl(&e, None, &mut ns));
     assert_eq!(ns.enums.last().unwrap().ty, Type::Uint(8));
 
     for i in 1..256 {
@@ -352,7 +327,7 @@ fn enum_256values_is_uint8() {
     assert_eq!(e.values.len(), 256);
 
     e.name.name = "foo2".to_owned();
-    assert!(enum_decl(&e, None, &mut ns, &mut Vec::new()));
+    assert!(enum_decl(&e, None, &mut ns));
     assert_eq!(ns.enums.last().unwrap().ty, Type::Uint(8));
 
     e.values.push(pt::Identifier {
@@ -361,6 +336,6 @@ fn enum_256values_is_uint8() {
     });
 
     e.name.name = "foo3".to_owned();
-    assert!(enum_decl(&e, None, &mut ns, &mut Vec::new()));
+    assert!(enum_decl(&e, None, &mut ns));
     assert_eq!(ns.enums.last().unwrap().ty, Type::Uint(16));
 }

+ 19 - 79
src/resolver/variables.rs → src/sema/variables.rs

@@ -1,34 +1,25 @@
-use super::{ContractVariable, Namespace, Symbol};
+use super::ast::{ContractVariable, ContractVariableType, Namespace, Symbol};
 use output::Output;
 use parser::pt;
-use resolver::cfg::{ControlFlowGraph, Instr, Storage, Vartable};
-use resolver::expression::{cast, expression, Expression};
-use resolver::ContractVariableType;
+use sema::expression::{cast, expression};
+use sema::symtable::Symtable;
 
 pub fn contract_variables(
     def: &pt::ContractDefinition,
     contract_no: usize,
     ns: &mut Namespace,
-    errors: &mut Vec<Output>,
 ) -> bool {
     let mut broken = false;
-    let mut vartab = Vartable::new();
-    let mut cfg = ControlFlowGraph::new();
+    let mut symtable = Symtable::new();
 
     for parts in &def.parts {
         if let pt::ContractPart::ContractVariableDefinition(ref s) = parts {
-            if !var_decl(s, contract_no, ns, &mut cfg, &mut vartab, errors) {
+            if !var_decl(s, contract_no, ns, &mut symtable) {
                 broken = true;
             }
         }
     }
 
-    cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
-
-    cfg.vars = vartab.drain();
-
-    ns.contracts[contract_no].initializer = cfg;
-
     broken
 }
 
@@ -36,11 +27,9 @@ fn var_decl(
     s: &pt::ContractVariableDefinition,
     contract_no: usize,
     ns: &mut Namespace,
-    cfg: &mut ControlFlowGraph,
-    vartab: &mut Vartable,
-    errors: &mut Vec<Output>,
+    symtable: &mut Symtable,
 ) -> bool {
-    let ty = match ns.resolve_type(Some(contract_no), false, &s.ty, errors) {
+    let ty = match ns.resolve_type(Some(contract_no), false, &s.ty) {
         Ok(s) => s,
         Err(()) => {
             return false;
@@ -54,7 +43,7 @@ fn var_decl(
         match &attr {
             pt::VariableAttribute::Constant(loc) => {
                 if is_constant {
-                    errors.push(Output::warning(
+                    ns.diagnostics.push(Output::warning(
                         *loc,
                         "duplicate constant attribute".to_string(),
                     ));
@@ -62,7 +51,7 @@ fn var_decl(
                 is_constant = true;
             }
             pt::VariableAttribute::Visibility(pt::Visibility::External(loc)) => {
-                errors.push(Output::error(
+                ns.diagnostics.push(Output::error(
                     *loc,
                     "variable cannot be declared external".to_string(),
                 ));
@@ -70,7 +59,7 @@ fn var_decl(
             }
             pt::VariableAttribute::Visibility(v) => {
                 if let Some(e) = &visibility {
-                    errors.push(Output::error_with_note(
+                    ns.diagnostics.push(Output::error_with_note(
                         v.loc(),
                         format!("variable visibility redeclared `{}'", v.to_string()),
                         e.loc(),
@@ -95,30 +84,17 @@ fn var_decl(
         ns.contracts[contract_no].top_of_contract_storage += slots;
         ContractVariableType::Storage(storage)
     } else {
-        ContractVariableType::Constant(ns.contracts[contract_no].constants.len())
+        ContractVariableType::Constant
     };
 
     let initializer = if let Some(initializer) = &s.initializer {
-        let expr = if is_constant {
-            expression(&initializer, cfg, Some(contract_no), ns, &mut None, errors)
-        } else {
-            expression(
-                &initializer,
-                cfg,
-                Some(contract_no),
-                ns,
-                &mut Some(vartab),
-                errors,
-            )
-        };
-
-        let (res, resty) = match expr {
-            Ok((res, ty)) => (res, ty),
+        let res = match expression(&initializer, Some(contract_no), ns, &symtable, is_constant) {
+            Ok(res) => res,
             Err(()) => return false,
         };
 
-        // implicityly conversion to correct ty
-        let res = match cast(&s.loc, res, &resty, &ty, true, ns, errors) {
+        // implicitly conversion to correct ty
+        let res = match cast(&s.loc, res, &ty, true, ns) {
             Ok(res) => res,
             Err(_) => return false,
         };
@@ -126,7 +102,7 @@ fn var_decl(
         Some(res)
     } else {
         if is_constant {
-            errors.push(Output::decl_error(
+            ns.diagnostics.push(Output::decl_error(
                 s.loc,
                 "missing initializer for constant".to_string(),
             ));
@@ -140,50 +116,14 @@ fn var_decl(
         name: s.name.name.to_string(),
         doc: s.doc.clone(),
         visibility,
-        ty: ty.clone(),
+        ty,
         var,
+        initializer,
     };
 
     let pos = ns.contracts[contract_no].variables.len();
 
     ns.contracts[contract_no].variables.push(sdecl);
 
-    if !ns.add_symbol(
-        Some(contract_no),
-        &s.name,
-        Symbol::Variable(s.loc, pos),
-        errors,
-    ) {
-        return false;
-    }
-
-    if let Some(res) = initializer {
-        if is_constant {
-            ns.contracts[contract_no].constants.push(res);
-        } else {
-            let var = vartab.find(&s.name, contract_no, ns, errors).unwrap();
-            let loc = res.loc();
-
-            cfg.add(
-                vartab,
-                Instr::Set {
-                    res: var.pos,
-                    expr: res,
-                },
-            );
-
-            if let Storage::Contract(offset) = &var.storage {
-                cfg.add(
-                    vartab,
-                    Instr::SetStorage {
-                        ty,
-                        local: var.pos,
-                        storage: Expression::NumberLiteral(loc, 256, offset.clone()),
-                    },
-                );
-            }
-        }
-    }
-
-    true
+    ns.add_symbol(Some(contract_no), &s.name, Symbol::Variable(s.loc, pos))
 }

+ 2 - 2
tests/loops.rs

@@ -12,7 +12,7 @@ fn first_error(errors: Vec<output::Output>) -> String {
 
 #[test]
 fn test_infinite_loop() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test3 {
             // The resolver should figure out how many breaks there
             // in the for loop; if there are none, then the basic block
@@ -26,5 +26,5 @@ fn test_infinite_loop() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "unreachable statement");
+    assert_eq!(first_error(ns.diagnostics), "unreachable statement");
 }

+ 69 - 63
tests/substrate_arrays/mod.rs

@@ -13,7 +13,7 @@ struct Val8(u8);
 
 #[test]
 fn missing_array_index() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public returns (uint) {
@@ -25,9 +25,12 @@ fn missing_array_index() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "expected expression before ‘]’ token");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "expected expression before ‘]’ token"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public returns (uint8) {
@@ -40,7 +43,7 @@ fn missing_array_index() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from uint8[5] to uint8[4] not possible"
     );
 }
@@ -217,7 +220,7 @@ fn enum_arrays() {
 
 #[test]
 fn data_locations() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function bar(uint storage) public returns () {
@@ -227,11 +230,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "data location ‘storage’ can only be specified for array, struct or mapping"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function bar(uint calldata x) public returns () {
@@ -241,11 +244,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "data location ‘calldata’ can only be specified for array, struct or mapping"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -256,11 +259,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "data location ‘memory’ can only be specified for array, struct or mapping"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -271,11 +274,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "data location ‘calldata’ can only be specified for array, struct or mapping"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -286,11 +289,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "data location ‘calldata’ can only be specified for array, struct or mapping"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -301,11 +304,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "data location ‘storage’ can only be specified for array, struct or mapping"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -316,11 +319,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "parameter of type ‘storage’ not allowed public or external functions"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -331,11 +334,11 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "return type of type ‘storage’ not allowed public or external functions"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum foo2 { bar1, bar2 }
@@ -346,7 +349,7 @@ fn data_locations() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "return type of type ‘storage’ not allowed public or external functions"
     );
 }
@@ -501,7 +504,7 @@ fn memory_to_storage() {
 
 #[test]
 fn array_dimensions() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             bool[10 - 10] x;
@@ -509,9 +512,9 @@ fn array_dimensions() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "zero size array not permitted");
+    assert_eq!(first_error(ns.diagnostics), "zero size array not permitted");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             bool[-10 + 10] x;
@@ -519,9 +522,9 @@ fn array_dimensions() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "zero size array not permitted");
+    assert_eq!(first_error(ns.diagnostics), "zero size array not permitted");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             bool[1 / 10] x;
@@ -529,9 +532,9 @@ fn array_dimensions() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "zero size array not permitted");
+    assert_eq!(first_error(ns.diagnostics), "zero size array not permitted");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             enum e { e1, e2, e3 }
@@ -540,9 +543,9 @@ fn array_dimensions() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "divide by zero");
+    assert_eq!(first_error(ns.diagnostics), "divide by zero");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             struct bar { 
@@ -553,7 +556,7 @@ fn array_dimensions() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "divide by zero");
+    assert_eq!(first_error(ns.diagnostics), "divide by zero");
 
     let mut runtime = build_solidity(
         r##"
@@ -707,7 +710,7 @@ fn struct_array_struct_abi() {
 
 #[test]
 fn memory_dynamic_array_new() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -720,11 +723,11 @@ fn memory_dynamic_array_new() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new dynamic array should have a single length argument"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -737,11 +740,11 @@ fn memory_dynamic_array_new() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new dynamic array should have a single length argument"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -754,11 +757,11 @@ fn memory_dynamic_array_new() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new size argument must be unsigned integer, not ‘bytes1’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -771,11 +774,11 @@ fn memory_dynamic_array_new() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new size argument must be unsigned integer, not ‘int8’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -787,7 +790,10 @@ fn memory_dynamic_array_new() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "new cannot allocate type ‘bool’");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "new cannot allocate type ‘bool’"
+    );
 
     let mut runtime = build_solidity(
         r#"
@@ -819,7 +825,7 @@ fn memory_dynamic_array_new() {
 
 #[test]
 fn memory_dynamic_array_deref() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -832,11 +838,11 @@ fn memory_dynamic_array_deref() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "array subscript must be an unsigned integer, not ‘int8’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             function test() public {
@@ -850,7 +856,7 @@ fn memory_dynamic_array_deref() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "array subscript must be an unsigned integer, not ‘int32’"
     );
 
@@ -1010,7 +1016,7 @@ fn storage_dynamic_array_length() {
 
 #[test]
 fn storage_dynamic_array_push() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             int32[] bar;
@@ -1024,11 +1030,11 @@ fn storage_dynamic_array_push() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "method ‘push()’ takes at most 1 argument"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             int32[4] bar;
@@ -1041,7 +1047,7 @@ fn storage_dynamic_array_push() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "method ‘push()’ not allowed on fixed length array"
     );
 
@@ -1090,7 +1096,7 @@ fn storage_dynamic_array_push() {
 
     runtime.function("test", Vec::new());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             struct s {
@@ -1107,14 +1113,14 @@ fn storage_dynamic_array_push() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function or method does not return a value"
     );
 }
 
 #[test]
 fn storage_dynamic_array_pop() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             int32[] bar;
@@ -1128,11 +1134,11 @@ fn storage_dynamic_array_pop() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "method ‘pop()’ does not take any arguments"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             int32[4] bar;
@@ -1145,7 +1151,7 @@ fn storage_dynamic_array_pop() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "method ‘pop()’ not allowed on fixed length array"
     );
 
@@ -1213,7 +1219,7 @@ fn storage_dynamic_array_pop() {
     assert_eq!(runtime.store.len(), 1);
 
     // pop returns the dereferenced value, not a reference to storage
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             struct s {
@@ -1230,14 +1236,14 @@ fn storage_dynamic_array_pop() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from struct foo.s to struct foo.s storage not possible"
     );
 }
 
 #[test]
 fn storage_delete() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             int32[] bar;
@@ -1250,11 +1256,11 @@ fn storage_delete() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "argument to ‘delete’ should be storage reference"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract foo {
             int32[] bar;
@@ -1267,8 +1273,8 @@ fn storage_delete() {
     );
 
     assert_eq!(
-        first_error(errors),
-        "function or method does not return a value"
+        first_error(ns.diagnostics),
+        "delete not allowed in expression"
     );
 
     // ensure that structs and fixed arrays are wiped by pop

+ 49 - 46
tests/substrate_calls/mod.rs

@@ -95,7 +95,7 @@ fn require() {
 
 #[test]
 fn contract_type() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -116,11 +116,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion to address from contract printer not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -130,9 +130,9 @@ fn contract_type() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -149,11 +149,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion from uint8 to address not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -170,11 +170,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from uint8 to contract printer not possible"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public returns (printer) {
@@ -185,11 +185,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new cannot construct current contract ‘printer’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public returns (printer) {
@@ -200,7 +200,7 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new cannot construct current contract ‘printer’"
     );
 }
@@ -286,7 +286,7 @@ fn contract_already_exists() {
 
 #[test]
 fn try_catch_external_calls() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -311,11 +311,11 @@ fn try_catch_external_calls() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "try returns list has 2 entries while function returns 1 values"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -340,7 +340,7 @@ fn try_catch_external_calls() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "type ‘int256[2] storage’ does not match return value of function ‘bool’"
     );
 
@@ -395,7 +395,7 @@ fn try_catch_external_calls() {
 
     runtime.function("test", Vec::new());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -420,11 +420,11 @@ fn try_catch_external_calls() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "catch can only take ‘bytes memory’, not ‘string’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -448,9 +448,9 @@ fn try_catch_external_calls() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "x is already declared");
+    assert_eq!(first_error(ns.diagnostics), "x is already declared");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -477,11 +477,11 @@ fn try_catch_external_calls() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "only catch ‘Error’ is supported, not ‘Foo’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -508,7 +508,7 @@ fn try_catch_external_calls() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "catch Error(...) can only take ‘string memory’, not ‘bytes’"
     );
 
@@ -631,7 +631,7 @@ fn try_catch_external_calls() {
 
 #[test]
 fn try_catch_constructor() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -654,11 +654,11 @@ fn try_catch_constructor() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "type ‘int32’ does not match return value of function ‘contract other’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -681,7 +681,7 @@ fn try_catch_constructor() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "constructor returns single contract, not 2 values"
     );
 
@@ -762,7 +762,7 @@ fn try_catch_constructor() {
 
     runtime.function("test", Vec::new());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -785,11 +785,11 @@ fn try_catch_constructor() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "try only supports external calls or constructor calls"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function f() public {
@@ -800,11 +800,11 @@ fn try_catch_constructor() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "expected code block, not list of named arguments"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -824,9 +824,12 @@ fn try_catch_constructor() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "code block missing for no catch");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "code block missing for no catch"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -850,9 +853,9 @@ fn try_catch_constructor() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "unexpected code block");
+    assert_eq!(first_error(ns.diagnostics), "unexpected code block");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(other o) public {
@@ -876,7 +879,7 @@ fn try_catch_constructor() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "unexpected code block");
+    assert_eq!(first_error(ns.diagnostics), "unexpected code block");
 }
 
 #[test]
@@ -1081,7 +1084,7 @@ fn payable_functions() {
 
     assert_eq!(runtime.vm.scratch, Ret(2).encode());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             receive() public {
@@ -1093,11 +1096,11 @@ fn payable_functions() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "receive function must be declared external"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             receive() external  {
@@ -1109,11 +1112,11 @@ fn payable_functions() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "receive function must be declared payable"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             fallback() payable external {
@@ -1125,11 +1128,11 @@ fn payable_functions() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "fallback function must not be declare payable, use ‘receive() external payable’ instead"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             fallback() public {
@@ -1141,7 +1144,7 @@ fn payable_functions() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "fallback function must be declared external"
     );
 }

+ 43 - 40
tests/substrate_contracts/mod.rs

@@ -9,7 +9,7 @@ struct RevertReturn(u32, String);
 
 #[test]
 fn contract_name() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function test() public {}
         }",
@@ -17,11 +17,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function cannot have same name as the contract"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             enum test { a}
         }",
@@ -29,11 +29,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "test is already defined as a contract name"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             bool test;
         }",
@@ -41,11 +41,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "test is already defined as a contract name"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             struct test { bool a; }
         }",
@@ -53,11 +53,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "test is already defined as a contract name"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function f() public {
                 int test;
@@ -67,11 +67,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "declaration of `test\' shadows contract name"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function f(int test) public {
             }
@@ -80,11 +80,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "declaration of `test\' shadows contract name"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function f() public returns (int test) {
                 return 0;
@@ -94,11 +94,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "declaration of `test\' shadows contract name"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract a {
             function x() public {
@@ -116,11 +116,11 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "circular reference creating contract ‘a’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract a {
             function x() public {
@@ -144,14 +144,14 @@ fn contract_name() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "circular reference creating contract ‘a’"
     );
 }
 
 #[test]
 fn contract_type() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -172,11 +172,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion to address from contract printer not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -186,9 +186,9 @@ fn contract_type() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -205,11 +205,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion from uint8 to address not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public {
@@ -226,11 +226,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from uint8 to contract printer not possible"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public returns (printer) {
@@ -241,11 +241,11 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new cannot construct current contract ‘printer’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract printer {
             function test() public returns (printer) {
@@ -256,14 +256,14 @@ fn contract_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new cannot construct current contract ‘printer’"
     );
 }
 
 #[test]
 fn external_call() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             b x;
@@ -281,11 +281,11 @@ fn external_call() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function expects 1 arguments, 0 provided"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             b x;
@@ -303,11 +303,11 @@ fn external_call() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function expects 2 arguments, 1 provided"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             b x;
@@ -331,7 +331,10 @@ fn external_call() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "duplicate argument with name ‘t’");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "duplicate argument with name ‘t’"
+    );
 
     #[derive(Debug, PartialEq, Encode, Decode)]
     struct Ret(u32);
@@ -497,7 +500,7 @@ fn external_datatypes() {
 
 #[test]
 fn creation_code() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test() public {
@@ -517,11 +520,11 @@ fn creation_code() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "circular reference creating contract ‘a’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test() public {
@@ -532,7 +535,7 @@ fn creation_code() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "containing our own contract code for ‘a’ would generate infinite size contract"
     );
 

+ 10 - 7
tests/substrate_enums/mod.rs

@@ -84,7 +84,7 @@ fn enums_other_contracts() {
 
 #[test]
 fn test_cast_errors() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             enum state { foo, bar, baz }
             function foo() public pure returns (uint8) {
@@ -95,11 +95,11 @@ fn test_cast_errors() {
     );
 
     assert_eq!(
-        first_error(errors),
-        "conversion from enum test.state to uint8 not possible"
+        first_error(ns.diagnostics),
+        "implicit conversion from enum test.state to uint8 not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             enum state {  }
             function foo() public pure returns (uint8) {
@@ -109,9 +109,12 @@ fn test_cast_errors() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "enum ‘state’ is missing fields");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "enum ‘state’ is missing fields"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             enum state { foo, bar, baz }
             function foo() public pure returns (uint8) {
@@ -121,5 +124,5 @@ fn test_cast_errors() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 }

+ 25 - 25
tests/substrate_expressions/mod.rs

@@ -243,7 +243,7 @@ fn expressions() {
 
 #[test]
 fn test_cast_errors() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foo(uint bar) public {
                 bool is_nonzero = bar;
@@ -253,11 +253,11 @@ fn test_cast_errors() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from uint256 to bool not possible"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foobar(uint foo, int bar) public returns (bool) {
                 return (foo < bar);
@@ -267,11 +267,11 @@ fn test_cast_errors() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would change sign from uint256 to int256"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foobar(int32 foo, uint16 bar) public returns (bool) {
                 foo = bar;
@@ -281,10 +281,10 @@ fn test_cast_errors() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
     // int16 can be negative, so cannot be stored in uint32
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foobar(uint32 foo, int16 bar) public returns (bool) {
                 foo = bar;
@@ -295,11 +295,11 @@ fn test_cast_errors() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would change sign from int16 to uint32"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract foo {
             uint bar;
 
@@ -331,7 +331,7 @@ fn test_cast_errors() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 }
 
 #[test]
@@ -952,7 +952,7 @@ fn power() {
 
     assert_eq!(runtime.vm.scratch, Val(0).encode());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function power(uint64 base, int64 exp) public returns (uint64) {
                 return base ** exp;
@@ -962,11 +962,11 @@ fn power() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "exponation (**) is not allowed with signed types"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function power(int64 base, uint64 exp) public returns (int64) {
                 return base ** exp;
@@ -976,11 +976,11 @@ fn power() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "exponation (**) is not allowed with signed types"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function power(int64 base, int64 exp) public returns (int64) {
                 return base ** exp;
@@ -990,7 +990,7 @@ fn power() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "exponation (**) is not allowed with signed types"
     );
 }
@@ -1354,7 +1354,7 @@ fn div() {
 
 #[test]
 fn destructure() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foo(uint bar) public {
                 int a;
@@ -1367,11 +1367,11 @@ fn destructure() {
     );
 
     assert_eq!(
-        first_error(errors),
-        "destructuring assignment has 2 values on the left and 3 on the right"
+        first_error(ns.diagnostics),
+        "destructuring assignment has 2 elements on the left and 3 on the right"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foo(uint bar) public {
                 int a;
@@ -1383,9 +1383,9 @@ fn destructure() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "`c\' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`c\' is not declared");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foo(uint bar) public {
                 int a;
@@ -1398,11 +1398,11 @@ fn destructure() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "storage modifier ‘memory’ not permitted on assignment"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foo(uint bar) public {
                 int a;
@@ -1414,7 +1414,7 @@ fn destructure() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "stray comma");
+    assert_eq!(first_error(ns.diagnostics), "stray comma");
 
     // The minus sign can be a unary negative or subtract.
     let mut runtime = build_solidity(

+ 51 - 48
tests/substrate_functions/mod.rs

@@ -87,7 +87,7 @@ fn constructor_wrong_selector() {
 
 #[test]
 fn fallback() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract test {
             int64 result = 102;
@@ -104,7 +104,7 @@ fn fallback() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function is missing a name. did you mean ‘fallback() extern {…}’ or ‘receive() extern {…}’?"
     );
 
@@ -214,7 +214,7 @@ fn test_overloading() {
 
 #[test]
 fn mutability() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             int64 foo = 1844674;
 
@@ -226,11 +226,11 @@ fn mutability() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function declared pure but reads contract storage"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             int64 foo = 1844674;
 
@@ -242,7 +242,7 @@ fn mutability() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function declared view but writes contract storage"
     );
 }
@@ -269,10 +269,10 @@ fn shadowing() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "declaration of `result\' shadows state variable"
     );
 
@@ -309,9 +309,9 @@ fn scopes() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "`a\' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`a\' is not declared");
 
     let src = "
     contract test {
@@ -323,9 +323,9 @@ fn scopes() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "`i\' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`i\' is not declared");
 }
 
 #[test]
@@ -340,9 +340,9 @@ fn for_forever() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "unreachable statement");
+    assert_eq!(first_error(ns.diagnostics), "unreachable statement");
 }
 
 #[test]
@@ -466,9 +466,9 @@ fn args_and_returns() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "arg1 is already declared");
+    assert_eq!(first_error(ns.diagnostics), "arg1 is already declared");
 
     let src = "
     contract args {
@@ -476,9 +476,9 @@ fn args_and_returns() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "arg2 is already declared");
+    assert_eq!(first_error(ns.diagnostics), "arg2 is already declared");
 
     let src = "
     contract args {
@@ -486,9 +486,9 @@ fn args_and_returns() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "missing return statement");
+    assert_eq!(first_error(ns.diagnostics), "missing return statement");
 
     let mut runtime = build_solidity(
         "
@@ -525,10 +525,10 @@ fn named_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function expects 2 arguments, 1 provided"
     );
 
@@ -542,9 +542,9 @@ fn named_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "unexpected array type");
+    assert_eq!(first_error(ns.diagnostics), "unexpected array type");
 
     let src = "
     contract args {
@@ -556,9 +556,12 @@ fn named_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "duplicate argument with name ‘arg1’");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "duplicate argument with name ‘arg1’"
+    );
 
     let src = "
     contract args {
@@ -570,10 +573,10 @@ fn named_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "missing argument ‘arg2’ to function ‘foo’"
     );
 
@@ -587,10 +590,10 @@ fn named_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from bool to uint256 not possible"
     );
 
@@ -631,10 +634,10 @@ fn positional_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function expects 2 arguments, 1 provided"
     );
 
@@ -648,9 +651,9 @@ fn positional_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
-    assert_eq!(first_error(errors), "unexpected array type");
+    assert_eq!(first_error(ns.diagnostics), "unexpected array type");
 
     let src = "
     contract args {
@@ -662,10 +665,10 @@ fn positional_argument_call() {
         }
     }";
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from uint8 to bool not possible"
     );
 
@@ -778,10 +781,10 @@ fn payable() {
             }
         }"##;
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "internal or private function cannot be payable"
     );
 
@@ -796,10 +799,10 @@ fn payable() {
             }
         }"##;
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "internal or private function cannot be payable"
     );
 
@@ -814,10 +817,10 @@ fn payable() {
             }
         }"##;
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "receive function must be declared payable"
     );
 
@@ -832,10 +835,10 @@ fn payable() {
             }
         }"##;
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "fallback function must not be declare payable, use ‘receive() external payable’ instead"
     );
 
@@ -847,10 +850,10 @@ fn payable() {
             }
         }"##;
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "all constructors should be defined ‘payable’ or not"
     );
 
@@ -862,10 +865,10 @@ fn payable() {
             }
         }"##;
 
-    let (_, errors) = parse_and_resolve(&src, Target::Substrate);
+    let ns = parse_and_resolve(&src, Target::Substrate);
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "all constructors should be defined ‘payable’ or not"
     );
 }

+ 29 - 20
tests/substrate_mappings/mod.rs

@@ -8,7 +8,7 @@ use solang::{parse_and_resolve, Target};
 
 #[test]
 fn bad_mapping_declares() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             struct s {
@@ -25,9 +25,12 @@ fn bad_mapping_declares() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "mapping only allowed in storage");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "mapping only allowed in storage"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             mapping(uint[] => address) data;
@@ -35,9 +38,12 @@ fn bad_mapping_declares() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "key of mapping cannot be array type");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "key of mapping cannot be array type"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             struct foo {
@@ -48,9 +54,12 @@ fn bad_mapping_declares() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "key of mapping cannot be struct type");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "key of mapping cannot be struct type"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             mapping(int => address) data;
@@ -59,9 +68,9 @@ fn bad_mapping_declares() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘data’ is a contract variable");
+    assert_eq!(first_error(ns.diagnostics), "‘data’ is a contract variable");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function test(mapping(int => address) x) public {
@@ -72,11 +81,11 @@ fn bad_mapping_declares() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "parameter with mapping type must be of type ‘storage’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function test(mapping(int => address) storage x) public {
@@ -87,11 +96,11 @@ fn bad_mapping_declares() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "parameter of type ‘storage’ not allowed public or external functions"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function test() public returns (mapping(int => address) x) {
@@ -102,11 +111,11 @@ fn bad_mapping_declares() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "return type containing mapping must be of type ‘storage’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function test() public returns (mapping(int => address) storage x) {
@@ -117,11 +126,11 @@ fn bad_mapping_declares() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "return type of type ‘storage’ not allowed public or external functions"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function test() public {
@@ -133,11 +142,11 @@ fn bad_mapping_declares() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "new cannot allocate type ‘mapping(int256 => address)’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             mapping(uint => bool) data;
@@ -149,7 +158,7 @@ fn bad_mapping_declares() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "‘delete’ cannot be applied to mapping type"
     );
 }

+ 50 - 50
tests/substrate_primitives/mod.rs

@@ -59,7 +59,7 @@ fn various_constants() {
 
 #[test]
 fn test_literal_overflow() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint8 foo = 300;
         }",
@@ -67,11 +67,11 @@ fn test_literal_overflow() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would truncate from uint16 to uint8"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint16 foo = 0x10000;
         }",
@@ -79,11 +79,11 @@ fn test_literal_overflow() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would truncate from uint24 to uint16"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             int8 foo = 0x8_0;
         }",
@@ -91,38 +91,38 @@ fn test_literal_overflow() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would truncate from uint8 to int8"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             int8 foo = 127;
         }",
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             int8 foo = -128;
         }",
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint8 foo = 255;
         }",
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint8 foo = -1_30;
         }",
@@ -130,11 +130,11 @@ fn test_literal_overflow() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion cannot change negative number to uint8"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             int64 foo = 1844674_4073709551616;
         }",
@@ -142,7 +142,7 @@ fn test_literal_overflow() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would truncate from uint72 to int64"
     );
 }
@@ -234,7 +234,7 @@ fn bytes() {
 
 #[test]
 fn address() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             address  foo = 0x1844674_4073709551616;
         }",
@@ -242,20 +242,20 @@ fn address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion from uint80 to address not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             address foo = 0xa368df6dfcd5ba7b0bc108af09e98e4655e35a2c3b2e2d5e3eae6c6f7cd8d2d4;
         }",
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "address literal has incorrect checksum, expected ‘0xA368dF6DFCD5Ba7b0BC108AF09e98E4655e35A2c3B2e2D5E3Eae6c6f7CD8D2D4’");
+    assert_eq!(first_error(ns.diagnostics), "address literal has incorrect checksum, expected ‘0xA368dF6DFCD5Ba7b0BC108AF09e98E4655e35A2c3B2e2D5E3Eae6c6f7CD8D2D4’");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint256 foo = 0xA368dF6DFCD5Ba7b0BC108AF09e98E4655e35A2c3B2e2D5E3Eae6c6f7CD8D2D4;
         }",
@@ -263,11 +263,11 @@ fn address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion would truncate from address to uint256"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             address foo = 0xA368dF6DFCD5Ba7b0BC108AF09e98E4655e35A2c3B2e2D5E3Eae6c6f7CD8D2D4;
 
@@ -279,11 +279,11 @@ fn address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "expression of type address not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             address foo = 0xA368dF6DFCD5Ba7b0BC108AF09e98E4655e35A2c3B2e2D5E3Eae6c6f7CD8D2D4;
 
@@ -295,11 +295,11 @@ fn address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "expression of type address not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             address foo = 0xA368dF6DFCD5Ba7b0BC108AF09e98E4655e35A2c3B2e2D5E3Eae6c6f7CD8D2D4;
 
@@ -311,7 +311,7 @@ fn address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "expression of type address not allowed"
     );
 
@@ -356,7 +356,7 @@ fn address() {
 
 #[test]
 fn address_payable_type() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(address payable a) public {
@@ -366,9 +366,9 @@ fn address_payable_type() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(address a) public {
@@ -384,11 +384,11 @@ fn address_payable_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion to contract other from address not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(address payable a) public {
@@ -404,11 +404,11 @@ fn address_payable_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion to contract other from address payable not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(address payable a) public {
@@ -423,9 +423,9 @@ fn address_payable_type() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(address payable a) public {
@@ -435,9 +435,9 @@ fn address_payable_type() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(payable a) public {
@@ -448,12 +448,12 @@ fn address_payable_type() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "‘payable’ cannot be used for type declarations, only casting. use ‘address payable’"
     );
 
     // note: this is not possible in solc yet
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test(address a) public {
@@ -463,7 +463,7 @@ fn address_payable_type() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 }
 
 #[test]
@@ -522,7 +522,7 @@ fn type_name() {
     runtime.function("max_int", Vec::new());
     runtime.function("max_uint", Vec::new());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -533,7 +533,7 @@ fn type_name() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "type ‘bool’ does not have type function max"
     );
 }
@@ -571,7 +571,7 @@ fn units() {
 
     runtime.function("foo", Vec::new());
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -582,11 +582,11 @@ fn units() {
     );
 
     assert_eq!(
-        first_warning(errors),
+        first_warning(ns.diagnostics),
         "ethereum currency unit used while not targetting ethereum"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -597,11 +597,11 @@ fn units() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "hexadecimal numbers cannot be used with unit denominations"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract c {
             function test() public {
@@ -612,7 +612,7 @@ fn units() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "unit denominations can only be used with number literals"
     );
 }

+ 22 - 22
tests/substrate_strings/mod.rs

@@ -7,7 +7,7 @@ use solang::{parse_and_resolve, Target};
 
 #[test]
 fn basic_tests() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -20,11 +20,11 @@ fn basic_tests() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "array subscript is not permitted on string"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -35,11 +35,11 @@ fn basic_tests() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from string to bytes not possible"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -50,11 +50,11 @@ fn basic_tests() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "conversion from bytes to string not possible"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -64,9 +64,9 @@ fn basic_tests() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -76,7 +76,7 @@ fn basic_tests() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 }
 
 #[test]
@@ -648,7 +648,7 @@ fn bytes_memory_subscript() {
 
 #[test]
 fn string_escape() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -659,11 +659,11 @@ fn string_escape() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "\\x escape should be followed by two hex digits"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -674,11 +674,11 @@ fn string_escape() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "\\x escape should be followed by two hex digits"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -689,11 +689,11 @@ fn string_escape() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "\\x escape should be followed by two hex digits"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -704,11 +704,11 @@ fn string_escape() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "\\u escape should be followed by four hex digits"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -719,11 +719,11 @@ fn string_escape() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "\\u escape should be followed by four hex digits"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             function foo() public {
@@ -734,7 +734,7 @@ fn string_escape() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "\\u escape should be followed by four hex digits"
     );
 

+ 41 - 26
tests/substrate_structs/mod.rs

@@ -12,7 +12,7 @@ struct Val8(u8);
 
 #[test]
 fn parse_structs() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -24,11 +24,11 @@ fn parse_structs() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "struct ‘Foo’ has duplicate struct field ‘a’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -40,11 +40,11 @@ fn parse_structs() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "storage location ‘storage’ not allowed for struct field"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -56,11 +56,11 @@ fn parse_structs() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "storage location ‘calldata’ not allowed for struct field"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -72,11 +72,11 @@ fn parse_structs() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "storage location ‘memory’ not allowed for struct field"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -86,11 +86,11 @@ fn parse_structs() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "struct definition for ‘Foo’ has no fields"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -100,10 +100,10 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "type ‘boolean’ not found");
+    assert_eq!(first_error(ns.diagnostics), "type ‘boolean’ not found");
 
     // is it impossible to define recursive structs
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -114,10 +114,13 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘Foo’ has infinite size");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "struct ‘Foo’ has infinite size"
+    );
 
     // is it impossible to define recursive structs
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract c {
             s z;
@@ -136,10 +139,10 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘s’ has infinite size");
+    assert_eq!(first_error(ns.diagnostics), "struct ‘s’ has infinite size");
 
     // literal initializers
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -154,10 +157,13 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘Foo’ has 2 fields, not 0");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "struct ‘Foo’ has 2 fields, not 0"
+    );
 
     // literal initializers
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -172,10 +178,13 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘Foo’ has 2 fields, not 3");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "struct ‘Foo’ has 2 fields, not 3"
+    );
 
     // literal initializers
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -190,10 +199,13 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘Foo’ has 2 fields, not 0");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "struct ‘Foo’ has 2 fields, not 0"
+    );
 
     // literal initializers
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -208,10 +220,13 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘Foo’ has 2 fields, not 3");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "struct ‘Foo’ has 2 fields, not 3"
+    );
 
     // literal initializers
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r#"
         contract test_struct_parsing {
             struct Foo {
@@ -226,7 +241,7 @@ fn parse_structs() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "struct ‘Foo’ has no field ‘z’");
+    assert_eq!(first_error(ns.diagnostics), "struct ‘Foo’ has no field ‘z’");
 }
 
 #[test]

+ 53 - 38
tests/substrate_value/mod.rs

@@ -6,7 +6,7 @@ use solang::{parse_and_resolve, Target};
 
 #[test]
 fn external_call_value() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -24,9 +24,12 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘foo’ not a valid call parameter");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘foo’ not a valid call parameter"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -44,9 +47,12 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘foo’ not a valid call parameter");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘foo’ not a valid call parameter"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -64,9 +70,12 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘salt’ not valid for external calls");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘salt’ not valid for external calls"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -84,9 +93,12 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘value’ specified multiple times");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘value’ specified multiple times"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -104,9 +116,12 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘value’ specified multiple times");
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘value’ specified multiple times"
+    );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -125,11 +140,11 @@ fn external_call_value() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "code block found where list of call arguments expected, like ‘{gas: 5000}’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(b t) public {
@@ -147,9 +162,9 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "missing call arguments");
+    assert_eq!(first_error(ns.diagnostics), "missing call arguments");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(int32 l) public {
@@ -168,11 +183,11 @@ fn external_call_value() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "sending value to function ‘test’ which is not payable"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(int32 l) public {
@@ -191,11 +206,11 @@ fn external_call_value() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "sending value to function ‘test’ which is not payable"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(int32 l) public {
@@ -213,9 +228,9 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract a {
             function test(int32 l) public {
@@ -233,7 +248,7 @@ fn external_call_value() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
     let mut runtime = build_solidity(
         r##"
@@ -503,7 +518,7 @@ fn constructor_salt() {
 
 #[test]
 fn this_address() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             function step1() public returns (address payable) {
@@ -513,9 +528,9 @@ fn this_address() {
         Target::Substrate,
     );
 
-    no_errors(errors);
+    no_errors(ns.diagnostics);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             function step1() public returns (address) {
@@ -526,11 +541,11 @@ fn this_address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "implicit conversion to address from contract b not allowed"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             function step1(b other) public {
@@ -540,7 +555,7 @@ fn this_address() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "expression is not assignable");
+    assert_eq!(first_error(ns.diagnostics), "expression is not assignable");
 
     let mut runtime = build_solidity(
         r##"
@@ -612,7 +627,7 @@ fn this_address() {
 
     assert_eq!(runtime.vm.scratch, runtime.vm.address);
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             int32 s;
@@ -630,11 +645,11 @@ fn this_address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function ‘other’ is not ‘public’ or ‘extern’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             int32 s;
@@ -652,14 +667,14 @@ fn this_address() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "function ‘other’ is not ‘public’ or ‘extern’"
     );
 }
 
 #[test]
 fn balance() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             function step1(address j) public returns (uint128) {
@@ -670,11 +685,11 @@ fn balance() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "substrate can only retrieve balance of this, like ‘address(this).balance’"
     );
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             function step1(b j) public returns (uint128) {
@@ -684,9 +699,9 @@ fn balance() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "‘balance’ not found");
+    assert_eq!(first_error(ns.diagnostics), "‘balance’ not found");
 
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         r##"
         contract b {
             function step1(address payable j) public returns (uint128) {
@@ -697,7 +712,7 @@ fn balance() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "substrate can only retrieve balance of this, like ‘address(this).balance’"
     );
 

+ 12 - 12
tests/variables.rs

@@ -12,7 +12,7 @@ fn first_error(errors: Vec<output::Output>) -> String {
 
 #[test]
 fn test_variable_errors() {
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             // solc 0.4.25 compiles this to 30.
             function foo() public pure returns (int32) {
@@ -25,13 +25,13 @@ fn test_variable_errors() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "`b' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`b' is not declared");
 }
 
 #[test]
 fn test_variable_initializer_errors() {
     // cannot read contract storage in constant
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint x = 102;
             uint constant y = x + 5;
@@ -40,12 +40,12 @@ fn test_variable_initializer_errors() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "cannot read variable ‘x’ in constant expression"
     );
 
     // cannot read contract storage in constant
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             function foo() public pure returns (uint) {
                 return 102;
@@ -56,12 +56,12 @@ fn test_variable_initializer_errors() {
     );
 
     assert_eq!(
-        first_error(errors),
+        first_error(ns.diagnostics),
         "cannot call function in constant expression"
     );
 
     // cannot refer to variable declared later
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint x = y + 102;
             uint y = 102;
@@ -69,10 +69,10 @@ fn test_variable_initializer_errors() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "`y' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`y' is not declared");
 
     // cannot refer to variable declared later (constant)
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint x = y + 102;
             uint constant y = 102;
@@ -80,15 +80,15 @@ fn test_variable_initializer_errors() {
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "`y' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`y' is not declared");
 
     // cannot refer to yourself
-    let (_, errors) = parse_and_resolve(
+    let ns = parse_and_resolve(
         "contract test {
             uint x = x + 102;
         }",
         Target::Substrate,
     );
 
-    assert_eq!(first_error(errors), "`x' is not declared");
+    assert_eq!(first_error(ns.diagnostics), "`x' is not declared");
 }

Деякі файли не було показано, через те що забагато файлів було змінено