Browse Source

When the result of pop() is discarded, do not load it

Not only is it wasted cycles, if the element contains a mapping, it
cannot be loaded and generates broken code.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 3 years ago
parent
commit
0b9cc85d74
9 changed files with 178 additions and 137 deletions
  1. 1 1
      clippy.toml
  2. 18 11
      src/codegen/expression.rs
  3. 4 2
      src/codegen/storage.rs
  4. 6 6
      src/sema/builtin.rs
  5. 118 94
      src/sema/expression.rs
  6. 2 2
      src/sema/format.rs
  7. 2 2
      src/sema/mod.rs
  8. 25 17
      src/sema/statements.rs
  9. 2 2
      src/sema/variables.rs

+ 1 - 1
clippy.toml

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

+ 18 - 11
src/codegen/expression.rs

@@ -12,7 +12,7 @@ use crate::sema::ast::{
     Builtin, CallTy, Expression, FormatArg, Function, Namespace, Parameter, StringLocation, Type,
 };
 use crate::sema::eval::{eval_const_number, eval_const_rational};
-use crate::sema::expression::{bigint_to_expression, cast, cast_shift_arg};
+use crate::sema::expression::{bigint_to_expression, cast, cast_shift_arg, ResolveTo};
 use crate::Target;
 use num_bigint::BigInt;
 use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
@@ -331,7 +331,7 @@ pub fn expression(
                     &BigInt::from_u8(length).unwrap(),
                     ns,
                     &mut Vec::new(),
-                    Some(ty),
+                    ResolveTo::Type(ty),
                 )
                 .unwrap(),
                 Type::DynamicBytes => Expression::StorageArrayLength {
@@ -354,7 +354,8 @@ pub fn expression(
                         }
                     }
                     Some(length) => {
-                        bigint_to_expression(loc, length, ns, &mut Vec::new(), Some(ty)).unwrap()
+                        bigint_to_expression(loc, length, ns, &mut Vec::new(), ResolveTo::Type(ty))
+                            .unwrap()
                     }
                 },
                 _ => unreachable!(),
@@ -569,11 +570,11 @@ pub fn expression(
                 storage_slots_array_push(loc, args, cfg, contract_no, func, ns, vartab, opt)
             }
         }
-        Expression::Builtin(loc, _, Builtin::ArrayPop, args) => {
+        Expression::Builtin(loc, ty, Builtin::ArrayPop, args) => {
             if ns.target == Target::Solana || args[0].ty().is_storage_bytes() {
-                array_pop(loc, args, cfg, contract_no, func, ns, vartab, opt)
+                array_pop(loc, args, &ty[0], cfg, contract_no, func, ns, vartab, opt)
             } else {
-                storage_slots_array_pop(loc, args, cfg, contract_no, func, ns, vartab, opt)
+                storage_slots_array_pop(loc, args, &ty[0], cfg, contract_no, func, ns, vartab, opt)
             }
         }
         Expression::Builtin(_, _, Builtin::Assert, args) => {
@@ -2049,10 +2050,14 @@ fn array_subscript(
     let index_width = index_ty.bits(ns);
 
     let array_length = match array_ty.deref_any() {
-        Type::Bytes(n) => {
-            bigint_to_expression(&array.loc(), &BigInt::from(*n), ns, &mut Vec::new(), None)
-                .unwrap()
-        }
+        Type::Bytes(n) => bigint_to_expression(
+            &array.loc(),
+            &BigInt::from(*n),
+            ns,
+            &mut Vec::new(),
+            ResolveTo::Unknown,
+        )
+        .unwrap(),
         Type::Array(_, _) => match array_ty.array_length() {
             None => {
                 if let Type::StorageRef(_, _) = array_ty {
@@ -2075,7 +2080,9 @@ fn array_subscript(
                     Expression::DynamicArrayLength(*loc, Box::new(array.clone()))
                 }
             }
-            Some(l) => bigint_to_expression(loc, l, ns, &mut Vec::new(), None).unwrap(),
+            Some(l) => {
+                bigint_to_expression(loc, l, ns, &mut Vec::new(), ResolveTo::Unknown).unwrap()
+            }
         },
         Type::DynamicBytes => Expression::DynamicArrayLength(*loc, Box::new(array.clone())),
         _ => {

+ 4 - 2
src/codegen/storage.rs

@@ -157,6 +157,7 @@ pub fn storage_slots_array_push(
 pub fn storage_slots_array_pop(
     loc: &pt::Loc,
     args: &[Expression],
+    return_ty: &Type,
     cfg: &mut ControlFlowGraph,
     contract_no: usize,
     func: Option<&Function>,
@@ -244,7 +245,7 @@ pub fn storage_slots_array_pop(
         },
     );
 
-    let val = if !elem_ty.contains_mapping(ns) {
+    let val = if *return_ty != Type::Void {
         let res_pos = vartab.temp_anonymous(&elem_ty);
 
         let expr = load_storage(
@@ -342,6 +343,7 @@ pub fn array_push(
 pub fn array_pop(
     loc: &pt::Loc,
     args: &[Expression],
+    return_ty: &Type,
     cfg: &mut ControlFlowGraph,
     contract_no: usize,
     func: Option<&Function>,
@@ -353,7 +355,7 @@ pub fn array_pop(
 
     let ty = args[0].ty().storage_array_elem().deref_into();
 
-    let res = if !ty.contains_mapping(ns) {
+    let res = if *return_ty != Type::Void {
         Some(vartab.temp_anonymous(&ty))
     } else {
         None

+ 6 - 6
src/sema/builtin.rs

@@ -1,6 +1,6 @@
 use super::ast::{Builtin, Diagnostic, Expression, Namespace, Type};
 use super::eval::eval_const_number;
-use super::expression::{cast, expression};
+use super::expression::{cast, expression, ResolveTo};
 use super::symtable::Symtable;
 use crate::parser::pt;
 use crate::Target;
@@ -547,7 +547,7 @@ pub fn resolve_call(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(&func.args[i]),
+                ResolveTo::Type(&func.args[i]),
             ) {
                 Ok(e) => e,
                 Err(()) => {
@@ -676,7 +676,7 @@ pub fn resolve_method_call(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::DynamicBytes),
+                ResolveTo::Type(&Type::DynamicBytes),
             )?,
             &Type::DynamicBytes,
             true,
@@ -770,7 +770,7 @@ pub fn resolve_method_call(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&Type::Bytes(4)),
+                    ResolveTo::Type(&Type::Bytes(4)),
                 )?;
 
                 resolved_args.insert(
@@ -806,7 +806,7 @@ pub fn resolve_method_call(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&Type::String),
+                    ResolveTo::Type(&Type::String),
                 )?;
 
                 resolved_args.insert(
@@ -843,7 +843,7 @@ pub fn resolve_method_call(
             false,
             unchecked,
             diagnostics,
-            None,
+            ResolveTo::Unknown,
         )?;
         let ty = expr.ty();
 

+ 118 - 94
src/sema/expression.rs

@@ -407,11 +407,11 @@ pub fn bigint_to_expression(
     n: &BigInt,
     ns: &Namespace,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let bits = n.bits();
 
-    if let Some(resolve_to) = resolve_to {
+    if let ResolveTo::Type(resolve_to) = resolve_to {
         if !resolve_to.is_integer() {
             diagnostics.push(Diagnostic::error(
                 *loc,
@@ -507,9 +507,9 @@ pub fn bigdecimal_to_expression(
     n: &BigRational,
     ns: &Namespace,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
-    if let Some(resolve_to) = resolve_to {
+    if let ResolveTo::Type(resolve_to) = resolve_to {
         if !resolve_to.is_rational() {
             diagnostics.push(Diagnostic::error(
                 *loc,
@@ -1335,6 +1335,14 @@ pub fn compatible_mutability(left: &Mutability, right: &Mutability) -> bool {
     )
 }
 
+/// When resolving an expression, what type are we looking for
+#[derive(PartialEq, Clone, Copy)]
+pub enum ResolveTo<'a> {
+    Unknown,        // We don't know what we're looking for, best effort
+    Discard,        // We won't be using the result. For example, an expression as a statement/
+    Type(&'a Type), // We will be wanting this type please, e.g. `int64 x = 1;`
+}
+
 /// Resolve a parsed expression into an AST expression. The resolve_to argument is a hint to what
 /// type the result should be.
 pub fn expression(
@@ -1347,7 +1355,7 @@ pub fn expression(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     match expr {
         pt::Expression::ArrayLiteral(loc, exprs) => {
@@ -1564,7 +1572,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
             let right = expression(
                 r,
@@ -1576,7 +1584,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
 
             check_var_usage_expression(ns, &left, &right, symtable);
@@ -1608,7 +1616,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
             let right = expression(
                 r,
@@ -1620,7 +1628,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
 
             check_var_usage_expression(ns, &left, &right, symtable);
@@ -1652,7 +1660,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
             let right = expression(
                 r,
@@ -1664,7 +1672,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
             check_var_usage_expression(ns, &left, &right, symtable);
 
@@ -1696,7 +1704,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
             let right = expression(
                 r,
@@ -1708,7 +1716,7 @@ pub fn expression(
                 is_constant,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Unknown,
             )?;
             check_var_usage_expression(ns, &left, &right, symtable);
 
@@ -2088,6 +2096,7 @@ pub fn expression(
             is_constant,
             unchecked,
             diagnostics,
+            resolve_to,
         ),
         pt::Expression::ArraySubscript(loc, _, None) => {
             diagnostics.push(Diagnostic::error(
@@ -2305,7 +2314,7 @@ fn string_literal(
     v: &[pt::StringLiteral],
     file_no: usize,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Expression {
     // Concatenate the strings
     let mut result = Vec::new();
@@ -2318,7 +2327,7 @@ fn string_literal(
 
     let length = result.len();
 
-    if let Some(Type::String) = resolve_to {
+    if let ResolveTo::Type(Type::String) = resolve_to {
         Expression::AllocDynamicArray(
             loc,
             Type::String,
@@ -2365,7 +2374,7 @@ fn hex_number_literal(
     n: &str,
     ns: &mut Namespace,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     // ns.address_length is in bytes; double for hex and two for the leading 0x
     if n.starts_with("0x") && !n.chars().any(|c| c == '_') && n.len() == 42 {
@@ -2407,7 +2416,7 @@ fn hex_number_literal(
     let s: String = n.chars().skip(2).filter(|v| *v != '_').collect();
 
     // hex values are allowed for bytesN but the length must match
-    if let Some(Type::Bytes(length)) = resolve_to {
+    if let ResolveTo::Type(Type::Bytes(length)) = resolve_to {
         let expected_length = *length as usize * 2;
         let val = BigInt::from_str_radix(&s, 16).unwrap();
 
@@ -2552,7 +2561,7 @@ fn variable(
     symtable: &mut Symtable,
     is_constant: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     if let Some(v) = symtable.find(&id.name) {
         return if is_constant {
@@ -2571,7 +2580,7 @@ fn variable(
     }
 
     // are we trying to resolve a function type?
-    let function_first = if let Some(resolve_to) = resolve_to {
+    let function_first = if let ResolveTo::Type(resolve_to) = resolve_to {
         matches!(
             resolve_to,
             Type::InternalFunction { .. } | Type::ExternalFunction { .. }
@@ -2684,7 +2693,7 @@ fn subtract(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -2757,7 +2766,7 @@ fn bitwise_or(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -2817,7 +2826,7 @@ fn bitwise_and(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -2877,7 +2886,7 @@ fn bitwise_xor(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -2937,7 +2946,7 @@ fn shift_left(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -2961,7 +2970,7 @@ fn shift_left(
         is_constant,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
 
     check_var_usage_expression(ns, &left, &right, symtable);
@@ -2992,7 +3001,7 @@ fn shift_right(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -3016,7 +3025,7 @@ fn shift_right(
         is_constant,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
 
     check_var_usage_expression(ns, &left, &right, symtable);
@@ -3048,7 +3057,7 @@ fn multiply(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -3101,7 +3110,7 @@ fn multiply(
     }
 
     // If we don't know what type the result is going to be, make any possible result fit.
-    if resolve_to.is_none() {
+    if resolve_to == ResolveTo::Unknown {
         let bits = std::cmp::min(256, ty.bits(ns) * 2);
 
         if ty.is_signed_int() {
@@ -3117,7 +3126,7 @@ fn multiply(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(&Type::Int(bits)),
+                ResolveTo::Type(&Type::Int(bits)),
             )
         } else {
             multiply(
@@ -3132,7 +3141,7 @@ fn multiply(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(&Type::Uint(bits)),
+                ResolveTo::Type(&Type::Uint(bits)),
             )
         }
     } else {
@@ -3158,7 +3167,7 @@ fn divide(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -3218,7 +3227,7 @@ fn modulo(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let left = expression(
         l,
@@ -3278,7 +3287,7 @@ fn power(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let mut base = expression(
         b,
@@ -3295,7 +3304,7 @@ fn power(
 
     // If we don't know what type the result is going to be, assume
     // the result is 256 bits
-    if resolve_to.is_none() {
+    if resolve_to == ResolveTo::Unknown {
         if base.ty().is_signed_int() {
             base = expression(
                 b,
@@ -3307,7 +3316,7 @@ fn power(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(&Type::Int(256)),
+                ResolveTo::Type(&Type::Int(256)),
             )?;
         } else {
             base = expression(
@@ -3320,7 +3329,7 @@ fn power(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(&Type::Uint(256)),
+                ResolveTo::Type(&Type::Uint(256)),
             )?;
         };
     }
@@ -3519,7 +3528,7 @@ pub fn match_constructor_to_args(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&params[i].ty),
+                ResolveTo::Type(&params[i].ty),
             ) {
                 Ok(v) => v,
                 Err(()) => {
@@ -3731,7 +3740,7 @@ pub fn constructor_named_args(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&param.ty),
+                ResolveTo::Type(&param.ty),
             ) {
                 Ok(e) => e,
                 Err(()) => {
@@ -3803,7 +3812,7 @@ pub fn type_name_expr(
     contract_no: Option<usize>,
     ns: &mut Namespace,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     if args.is_empty() {
         diagnostics.push(Diagnostic::error(
@@ -4024,7 +4033,7 @@ pub fn new(
         false,
         unchecked,
         diagnostics,
-        Some(&Type::Uint(32)),
+        ResolveTo::Type(&Type::Uint(32)),
     )?;
 
     used_variable(ns, &size_expr, symtable);
@@ -4063,7 +4072,7 @@ fn equal(
         is_constant,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     let right = expression(
         r,
@@ -4075,7 +4084,7 @@ fn equal(
         is_constant,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
 
     check_var_usage_expression(ns, &left, &right, symtable);
@@ -4176,7 +4185,7 @@ fn addition(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let mut left = expression(
         l,
@@ -4269,7 +4278,7 @@ fn addition(
     )?;
 
     // If we don't know what type the result is going to be
-    if resolve_to.is_none() {
+    if resolve_to == ResolveTo::Unknown {
         let bits = std::cmp::min(256, ty.bits(ns) * 2);
         let resolve_to = if ty.is_signed_int() {
             Type::Int(bits)
@@ -4287,7 +4296,7 @@ fn addition(
             is_constant,
             unchecked,
             diagnostics,
-            Some(&resolve_to),
+            ResolveTo::Type(&resolve_to),
         )?;
         right = expression(
             r,
@@ -4299,7 +4308,7 @@ fn addition(
             is_constant,
             unchecked,
             diagnostics,
-            Some(&resolve_to),
+            ResolveTo::Type(&resolve_to),
         )?;
     }
 
@@ -4335,7 +4344,7 @@ pub fn assign_single(
         false,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     assigned_variable(ns, &var, symtable);
 
@@ -4350,7 +4359,7 @@ pub fn assign_single(
         false,
         unchecked,
         diagnostics,
-        Some(var_ty.deref_any()),
+        ResolveTo::Type(var_ty.deref_any()),
     )?;
     used_variable(ns, &val, symtable);
     match &var {
@@ -4471,7 +4480,7 @@ fn assign_expr(
         false,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     assigned_variable(ns, &var, symtable);
     let var_ty = var.ty();
@@ -4480,9 +4489,9 @@ fn assign_expr(
         expr,
         pt::Expression::AssignShiftLeft(_, _, _) | pt::Expression::AssignShiftRight(_, _, _)
     ) {
-        None
+        ResolveTo::Unknown
     } else {
-        Some(var_ty.deref_any())
+        ResolveTo::Type(var_ty.deref_any())
     };
 
     let set = expression(
@@ -4710,7 +4719,7 @@ fn incr_decr(
         false,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     used_variable(ns, &var, symtable);
     let var_ty = var.ty();
@@ -4887,7 +4896,7 @@ fn member_access(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     // is it a builtin special variable like "block.timestamp"
     if let pt::Expression::Variable(namespace) = e {
@@ -5035,7 +5044,13 @@ fn member_access(
                         //So the variable is also assigned a value to be read from 'length'
                         assigned_variable(ns, &expr, symtable);
                         used_variable(ns, &expr, symtable);
-                        bigint_to_expression(loc, d, ns, diagnostics, Some(&Type::Uint(32)))
+                        bigint_to_expression(
+                            loc,
+                            d,
+                            ns,
+                            diagnostics,
+                            ResolveTo::Type(&Type::Uint(32)),
+                        )
                     }
                 };
             }
@@ -5262,7 +5277,7 @@ fn contract_constant(
     ns: &mut Namespace,
     symtable: &mut Symtable,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Option<Expression>, ()> {
     let namespace = match e {
         pt::Expression::Variable(namespace) => namespace,
@@ -5281,14 +5296,14 @@ fn contract_constant(
             .find(|(_, variable)| variable.name == id.name)
         {
             if !var.constant {
-                let resolve_function = resolve_to
-                    .map(|ty| {
-                        matches!(
-                            ty,
-                            Type::InternalFunction { .. } | Type::ExternalFunction { .. }
-                        )
-                    })
-                    .unwrap_or(false);
+                let resolve_function = if let ResolveTo::Type(ty) = resolve_to {
+                    matches!(
+                        ty,
+                        Type::InternalFunction { .. } | Type::ExternalFunction { .. }
+                    )
+                } else {
+                    false
+                };
 
                 if resolve_function {
                     // requested function, fall through
@@ -5344,7 +5359,7 @@ fn array_subscript(
         is_constant,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     let array_ty = array.ty();
 
@@ -5380,7 +5395,7 @@ fn array_subscript(
         is_constant,
         unchecked,
         diagnostics,
-        Some(&index_width_ty),
+        ResolveTo::Type(&index_width_ty),
     )?;
 
     let index_ty = index.ty();
@@ -5511,7 +5526,7 @@ fn struct_literal(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(&struct_def.fields[i].ty),
+                ResolveTo::Type(&struct_def.fields[i].ty),
             )?;
             used_variable(ns, &expr, symtable);
             fields.push(cast(
@@ -5556,7 +5571,7 @@ fn call_function_type(
         false,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
 
     let mut ty = function.ty();
@@ -5606,7 +5621,7 @@ fn call_function_type(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&params[i]),
+                ResolveTo::Type(&params[i]),
             )?;
 
             cast_args.push(cast(
@@ -5690,7 +5705,7 @@ fn call_function_type(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&params[i]),
+                ResolveTo::Type(&params[i]),
             )?;
 
             cast_args.push(cast(
@@ -5848,7 +5863,7 @@ pub fn call_position_args(
                 false,
                 unchecked,
                 &mut errors,
-                Some(&ty),
+                ResolveTo::Type(&ty),
             ) {
                 Ok(e) => e,
                 Err(_) => {
@@ -6020,7 +6035,7 @@ fn function_call_with_named_args(
                 false,
                 unchecked,
                 &mut errors,
-                Some(&ty),
+                ResolveTo::Type(&ty),
             ) {
                 Ok(e) => e,
                 Err(()) => {
@@ -6142,7 +6157,7 @@ fn named_struct_literal(
                         is_constant,
                         unchecked,
                         diagnostics,
-                        Some(&f.ty),
+                        ResolveTo::Type(&f.ty),
                     )?;
                     used_variable(ns, &expr, symtable);
                     fields[i] = cast(loc, expr, &f.ty, true, ns, diagnostics)?;
@@ -6179,6 +6194,7 @@ fn method_call_pos_args(
     ns: &mut Namespace,
     symtable: &mut Symtable,
     diagnostics: &mut Vec<Diagnostic>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     if let pt::Expression::Variable(namespace) = var {
         if builtin::is_builtin_call(Some(&namespace.name), &func.name, ns) {
@@ -6309,7 +6325,7 @@ fn method_call_pos_args(
         false,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     let var_ty = var_expr.ty();
 
@@ -6392,7 +6408,7 @@ fn method_call_pos_args(
                                 false,
                                 unchecked,
                                 diagnostics,
-                                Some(&elem_ty),
+                                ResolveTo::Type(&elem_ty),
                             )?;
 
                             builtin_args.push(cast(
@@ -6450,7 +6466,7 @@ fn method_call_pos_args(
                     let storage_elem = ty.storage_array_elem();
                     let elem_ty = storage_elem.deref_any();
 
-                    let return_ty = if elem_ty.contains_mapping(ns) {
+                    let return_ty = if resolve_to == ResolveTo::Discard {
                         Type::Void
                     } else {
                         elem_ty.clone()
@@ -6503,7 +6519,7 @@ fn method_call_pos_args(
                                 false,
                                 unchecked,
                                 diagnostics,
-                                Some(&elem_ty),
+                                ResolveTo::Type(&elem_ty),
                             )?;
 
                             builtin_args.push(cast(
@@ -6583,7 +6599,7 @@ fn method_call_pos_args(
                         false,
                         unchecked,
                         diagnostics,
-                        Some(elem_ty),
+                        ResolveTo::Type(elem_ty),
                     )?;
 
                     cast(&args[0].loc(), val_expr, elem_ty, true, ns, diagnostics)?
@@ -6683,7 +6699,7 @@ fn method_call_pos_args(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&ty),
+                    ResolveTo::Type(&ty),
                 ) {
                     Ok(e) => e,
                     Err(_) => {
@@ -6843,7 +6859,7 @@ fn method_call_pos_args(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Value),
+                ResolveTo::Type(&Type::Value),
             )?;
 
             let value = cast(&args[0].loc(), expr, &Type::Value, true, ns, diagnostics)?;
@@ -6919,7 +6935,7 @@ fn method_call_pos_args(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::DynamicBytes),
+                ResolveTo::Type(&Type::DynamicBytes),
             )?;
 
             let args = cast(
@@ -7071,7 +7087,7 @@ fn resolve_using(
                     false,
                     context.unchecked,
                     &mut errors,
-                    Some(&ty),
+                    ResolveTo::Type(&ty),
                 ) {
                     Ok(e) => e,
                     Err(()) => {
@@ -7268,7 +7284,7 @@ fn method_call_named_args(
         false,
         unchecked,
         diagnostics,
-        None,
+        ResolveTo::Unknown,
     )?;
     let var_ty = var_expr.ty();
 
@@ -7356,7 +7372,7 @@ fn method_call_named_args(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&param.ty),
+                    ResolveTo::Type(&param.ty),
                 ) {
                     Ok(e) => e,
                     Err(()) => {
@@ -7499,7 +7515,7 @@ fn resolve_array_literal(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
-    resolve_to: Option<&Type>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let mut dims = Box::new(Vec::new());
     let mut flattened = Vec::new();
@@ -7516,7 +7532,11 @@ fn resolve_array_literal(
 
     let mut flattened = flattened.iter();
 
-    let resolve_to = resolve_to.map(|ty| if let Type::Array(ty, _) = ty { ty } else { ty });
+    let resolve_to = if let ResolveTo::Type(Type::Array(elem_ty, _)) = resolve_to {
+        ResolveTo::Type(elem_ty)
+    } else {
+        resolve_to
+    };
 
     // We follow the solidity scheme were everthing gets implicitly converted to the
     // type of the first element
@@ -7533,7 +7553,7 @@ fn resolve_array_literal(
         resolve_to,
     )?;
 
-    let ty = if let Some(ty) = resolve_to {
+    let ty = if let ResolveTo::Type(ty) = resolve_to {
         ty.clone()
     } else {
         first.ty()
@@ -7553,7 +7573,7 @@ fn resolve_array_literal(
             is_constant,
             unchecked,
             diagnostics,
-            Some(&ty),
+            ResolveTo::Type(&ty),
         )?;
         used_variable(ns, &other, symtable);
 
@@ -7742,7 +7762,7 @@ fn parse_call_args(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&ty),
+                    ResolveTo::Type(&ty),
                 )?;
 
                 res.value = Some(Box::new(cast(
@@ -7777,7 +7797,7 @@ fn parse_call_args(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&ty),
+                    ResolveTo::Type(&ty),
                 )?;
 
                 res.gas = Some(Box::new(cast(
@@ -7821,7 +7841,7 @@ fn parse_call_args(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&ty),
+                    ResolveTo::Type(&ty),
                 )?;
 
                 res.space = Some(Box::new(cast(
@@ -7865,7 +7885,7 @@ fn parse_call_args(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&ty),
+                    ResolveTo::Type(&ty),
                 )?;
 
                 res.salt = Some(Box::new(cast(
@@ -7981,6 +8001,7 @@ pub fn call_expr(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let mut nullsink = Vec::new();
 
@@ -8025,7 +8046,7 @@ pub fn call_expr(
                     is_constant,
                     unchecked,
                     diagnostics,
-                    None,
+                    ResolveTo::Unknown,
                 )?;
 
                 cast(loc, expr, &to, false, ns, diagnostics)
@@ -8046,6 +8067,7 @@ pub fn call_expr(
         is_constant,
         unchecked,
         diagnostics,
+        resolve_to,
     )?;
 
     check_function_call(ns, &expr, symtable);
@@ -8073,6 +8095,7 @@ pub fn function_call_expr(
     is_constant: bool,
     unchecked: bool,
     diagnostics: &mut Vec<Diagnostic>,
+    resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     let (ty, call_args, call_args_loc) = collect_call_args(ty, diagnostics)?;
 
@@ -8100,6 +8123,7 @@ pub fn function_call_expr(
                 ns,
                 symtable,
                 diagnostics,
+                resolve_to,
             )
         }
         pt::Expression::Variable(id) => {
@@ -8336,7 +8360,7 @@ fn mapping_subscript(
                 is_constant,
                 unchecked,
                 diagnostics,
-                Some(key_ty),
+                ResolveTo::Type(key_ty),
             )?,
             key_ty,
             true,

+ 2 - 2
src/sema/format.rs

@@ -1,5 +1,5 @@
 use super::ast::{Diagnostic, Expression, FormatArg, Namespace, Type};
-use super::expression::{cast, expression};
+use super::expression::{cast, expression, ResolveTo};
 use super::symtable::Symtable;
 use crate::parser::pt;
 
@@ -37,7 +37,7 @@ pub fn string_format(
             false,
             unchecked,
             diagnostics,
-            None,
+            ResolveTo::Unknown,
         )?;
 
         let ty = expr.ty();

+ 2 - 2
src/sema/mod.rs

@@ -28,7 +28,7 @@ mod variables;
 
 use self::contracts::visit_bases;
 use self::eval::eval_const_number;
-use self::expression::expression;
+use self::expression::{expression, ResolveTo};
 use self::functions::{resolve_params, resolve_returns};
 use self::symtable::Symtable;
 use self::variables::var_decl;
@@ -1491,7 +1491,7 @@ impl ast::Namespace {
             true,
             true,
             diagnostics,
-            Some(&ast::Type::Uint(256)),
+            ResolveTo::Type(&ast::Type::Uint(256)),
         )?;
 
         match size_expr.ty() {

+ 25 - 17
src/sema/statements.rs

@@ -3,6 +3,7 @@ use super::contracts::is_base;
 use super::expression::{
     available_functions, call_expr, call_position_args, cast, constructor_named_args, expression,
     function_call_expr, match_constructor_to_args, named_call_expr, named_function_call_expr, new,
+    ResolveTo,
 };
 use super::symtable::{LoopScopes, Symtable};
 use crate::parser::pt;
@@ -320,7 +321,7 @@ fn statement(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&var_ty),
+                    ResolveTo::Type(&var_ty),
                 )?;
 
                 used_variable(ns, &expr, symtable);
@@ -426,7 +427,7 @@ fn statement(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Bool),
+                ResolveTo::Type(&Type::Bool),
             )?;
             used_variable(ns, &expr, symtable);
             let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
@@ -464,7 +465,7 @@ fn statement(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Bool),
+                ResolveTo::Type(&Type::Bool),
             )?;
             used_variable(ns, &expr, symtable);
             let cond = cast(&expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
@@ -501,7 +502,7 @@ fn statement(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Bool),
+                ResolveTo::Type(&Type::Bool),
             )?;
             used_variable(ns, &expr, symtable);
 
@@ -658,7 +659,7 @@ fn statement(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Bool),
+                ResolveTo::Type(&Type::Bool),
             )?;
 
             let cond = cast(&cond_expr.loc(), expr, &Type::Bool, true, ns, diagnostics)?;
@@ -771,7 +772,7 @@ fn statement(
                     false,
                     unchecked,
                     diagnostics,
-                    None,
+                    ResolveTo::Unknown,
                 )?;
                 used_variable(ns, &expr, symtable);
                 return if let Type::StorageRef(_, ty) = expr.ty() {
@@ -834,7 +835,7 @@ fn statement(
                 }
             }
 
-            // the rest
+            // the rest. We don't care about the result
             let expr = expression(
                 expr,
                 file_no,
@@ -845,7 +846,7 @@ fn statement(
                 false,
                 unchecked,
                 diagnostics,
-                None,
+                ResolveTo::Discard,
             )?;
 
             let reachable = expr.ty() != Type::Unreachable;
@@ -933,7 +934,7 @@ fn emit_event(
                     false,
                     unchecked,
                     &mut temp_diagnostics,
-                    None,
+                    ResolveTo::Unknown,
                 );
             }
 
@@ -977,7 +978,7 @@ fn emit_event(
                         false,
                         unchecked,
                         &mut temp_diagnostics,
-                        Some(&ty),
+                        ResolveTo::Type(&ty),
                     ) {
                         Ok(e) => e,
                         Err(()) => {
@@ -1053,7 +1054,7 @@ fn emit_event(
                     false,
                     unchecked,
                     &mut temp_diagnostics,
-                    None,
+                    ResolveTo::Unknown,
                 );
 
                 arguments.insert(&arg.name.name, &arg.expr);
@@ -1128,7 +1129,7 @@ fn emit_event(
                         false,
                         unchecked,
                         &mut temp_diagnostics,
-                        Some(&param.ty),
+                        ResolveTo::Type(&param.ty),
                     ) {
                         Ok(e) => e,
                         Err(()) => {
@@ -1232,7 +1233,7 @@ fn destructure(
                     false,
                     unchecked,
                     diagnostics,
-                    None,
+                    ResolveTo::Unknown,
                 )?;
 
                 match &e {
@@ -1365,6 +1366,7 @@ fn destructure_values(
                 false,
                 unchecked,
                 diagnostics,
+                ResolveTo::Unknown,
             )?;
             check_function_call(ns, &res, symtable);
             res
@@ -1396,7 +1398,7 @@ fn destructure_values(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Bool),
+                ResolveTo::Type(&Type::Bool),
             )?;
 
             used_variable(ns, &cond, symtable);
@@ -1465,7 +1467,11 @@ fn destructure_values(
                     false,
                     unchecked,
                     diagnostics,
-                    left_tys[i].as_ref(),
+                    if let Some(ty) = left_tys[i].as_ref() {
+                        ResolveTo::Type(ty)
+                    } else {
+                        ResolveTo::Unknown
+                    },
                 )?;
                 match e.ty() {
                     Type::Void | Type::Unreachable => {
@@ -1603,6 +1609,7 @@ fn return_with_values(
                 false,
                 unchecked,
                 diagnostics,
+                ResolveTo::Unknown,
             )?;
             used_variable(ns, &expr, symtable);
             expr
@@ -1636,7 +1643,7 @@ fn return_with_values(
                 false,
                 unchecked,
                 diagnostics,
-                Some(&Type::Bool),
+                ResolveTo::Type(&Type::Bool),
             )?;
             used_variable(ns, &cond, symtable);
 
@@ -1727,7 +1734,7 @@ fn return_with_values(
                     false,
                     unchecked,
                     diagnostics,
-                    Some(&return_ty),
+                    ResolveTo::Type(&return_ty),
                 )?;
                 let expr = cast(loc, expr, &return_ty, true, ns, diagnostics)?;
                 used_variable(ns, &expr, symtable);
@@ -1907,6 +1914,7 @@ fn try_catch(
                 false,
                 unchecked,
                 diagnostics,
+                ResolveTo::Unknown,
             )?;
             check_function_call(ns, &res, symtable);
             res

+ 2 - 2
src/sema/variables.rs

@@ -1,7 +1,7 @@
 use super::ast::{
     Diagnostic, Expression, Function, Namespace, Parameter, Statement, Symbol, Type, Variable,
 };
-use super::expression::{cast, expression};
+use super::expression::{cast, expression, ResolveTo};
 use super::symtable::Symtable;
 use super::tags::resolve_tags;
 use crate::parser::pt;
@@ -233,7 +233,7 @@ pub fn var_decl(
             is_constant,
             false,
             &mut diagnostics,
-            Some(&ty),
+            ResolveTo::Type(&ty),
         ) {
             Ok(res) => {
                 // implicitly conversion to correct ty