Explorar el Código

Partial support for fixed arrays

Still needs:
 - literal arrays
 - assign to array index
 - abi encoders/decoders
 - tests

Signed-off-by: Sean Young <sean@mess.org>
Sean Young hace 5 años
padre
commit
f6bc976edc

+ 1 - 0
build.rs

@@ -4,6 +4,7 @@ use std::process::Command;
 fn main() {
     lalrpop::Configuration::new()
         .generate_in_source_tree()
+        .emit_rerun_directives(true)
         .process()
         .unwrap();
 

+ 161 - 106
src/emit/mod.rs

@@ -5,6 +5,7 @@ use resolver::cfg;
 use std::path::Path;
 use std::str;
 
+use num_traits::ToPrimitive;
 use std::collections::HashMap;
 use std::collections::VecDeque;
 
@@ -14,7 +15,7 @@ use inkwell::memory_buffer::MemoryBuffer;
 use inkwell::module::{Linkage, Module};
 use inkwell::targets::{CodeModel, FileType, RelocMode, Target};
 use inkwell::types::BasicTypeEnum;
-use inkwell::types::{IntType, StringRadix};
+use inkwell::types::{BasicType, IntType, StringRadix};
 use inkwell::values::{
     BasicValueEnum, FunctionValue, GlobalValue, IntValue, PhiValue, PointerValue,
 };
@@ -238,16 +239,20 @@ impl<'a> Contract<'a> {
         e: &cfg::Expression,
         vartab: &[Variable<'a>],
         runtime: &dyn TargetRuntime,
-    ) -> IntValue<'a> {
+    ) -> BasicValueEnum<'a> {
         match e {
-            cfg::Expression::BoolLiteral(val) => {
-                self.context.bool_type().const_int(*val as u64, false)
-            }
+            cfg::Expression::BoolLiteral(val) => self
+                .context
+                .bool_type()
+                .const_int(*val as u64, false)
+                .into(),
             cfg::Expression::NumberLiteral(bits, n) => {
                 let ty = self.context.custom_width_int_type(*bits as _);
                 let s = n.to_string();
 
-                ty.const_int_from_string(&s, StringRadix::Decimal).unwrap()
+                ty.const_int_from_string(&s, StringRadix::Decimal)
+                    .unwrap()
+                    .into()
             }
             cfg::Expression::BytesLiteral(bs) => {
                 let ty = self.context.custom_width_int_type((bs.len() * 8) as u32);
@@ -257,22 +262,23 @@ impl<'a> Contract<'a> {
 
                 ty.const_int_from_string(&s, StringRadix::Hexadecimal)
                     .unwrap()
+                    .into()
             }
             cfg::Expression::Add(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_int_add(left, right, "")
+                self.builder.build_int_add(left, right, "").into()
             }
             cfg::Expression::Subtract(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_int_sub(left, right, "")
+                self.builder.build_int_sub(left, right, "").into()
             }
             cfg::Expression::Multiply(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 let bits = left.get_type().get_bit_width();
 
@@ -316,37 +322,40 @@ impl<'a> Contract<'a> {
                         "",
                     );
 
-                    self.builder.build_load(o, "mul").into_int_value()
+                    self.builder.build_load(o, "mul")
                 } else {
-                    self.builder.build_int_mul(left, right, "")
+                    self.builder.build_int_mul(left, right, "").into()
                 }
             }
             cfg::Expression::UDivide(l, r) => {
                 let left = self.expression(l, vartab, runtime);
                 let right = self.expression(r, vartab, runtime);
 
-                let bits = left.get_type().get_bit_width();
+                let bits = left.into_int_value().get_type().get_bit_width();
 
                 if bits > 64 {
                     let f = self.udivmod(bits, runtime);
 
-                    let rem = self.builder.build_alloca(left.get_type(), "");
+                    let rem = self
+                        .builder
+                        .build_alloca(left.into_int_value().get_type(), "");
 
                     self.builder
-                        .build_call(f, &[left.into(), right.into(), rem.into()], "udiv")
+                        .build_call(f, &[left, right, rem.into()], "udiv")
                         .try_as_basic_value()
                         .left()
                         .unwrap()
-                        .into_int_value()
                 } else {
-                    self.builder.build_int_unsigned_div(left, right, "")
+                    self.builder
+                        .build_int_unsigned_div(left.into_int_value(), right.into_int_value(), "")
+                        .into()
                 }
             }
             cfg::Expression::SDivide(l, r) => {
                 let left = self.expression(l, vartab, runtime);
                 let right = self.expression(r, vartab, runtime);
 
-                let bits = left.get_type().get_bit_width();
+                let bits = left.into_int_value().get_type().get_bit_width();
 
                 if bits > 64 {
                     let f = self.sdivmod(bits, runtime);
@@ -354,20 +363,21 @@ impl<'a> Contract<'a> {
                     let rem = self.builder.build_alloca(left.get_type(), "");
 
                     self.builder
-                        .build_call(f, &[left.into(), right.into(), rem.into()], "udiv")
+                        .build_call(f, &[left, right, rem.into()], "udiv")
                         .try_as_basic_value()
                         .left()
                         .unwrap()
-                        .into_int_value()
                 } else {
-                    self.builder.build_int_signed_div(left, right, "")
+                    self.builder
+                        .build_int_signed_div(left.into_int_value(), right.into_int_value(), "")
+                        .into()
                 }
             }
             cfg::Expression::UModulo(l, r) => {
                 let left = self.expression(l, vartab, runtime);
                 let right = self.expression(r, vartab, runtime);
 
-                let bits = left.get_type().get_bit_width();
+                let bits = left.into_int_value().get_type().get_bit_width();
 
                 if bits > 64 {
                     let f = self.udivmod(bits, runtime);
@@ -375,18 +385,20 @@ impl<'a> Contract<'a> {
                     let rem = self.builder.build_alloca(left.get_type(), "");
 
                     self.builder
-                        .build_call(f, &[left.into(), right.into(), rem.into()], "udiv");
+                        .build_call(f, &[left, right, rem.into()], "udiv");
 
-                    self.builder.build_load(rem, "urem").into_int_value()
+                    self.builder.build_load(rem, "urem")
                 } else {
-                    self.builder.build_int_unsigned_rem(left, right, "")
+                    self.builder
+                        .build_int_unsigned_rem(left.into_int_value(), right.into_int_value(), "")
+                        .into()
                 }
             }
             cfg::Expression::SModulo(l, r) => {
                 let left = self.expression(l, vartab, runtime);
                 let right = self.expression(r, vartab, runtime);
 
-                let bits = left.get_type().get_bit_width();
+                let bits = left.into_int_value().get_type().get_bit_width();
 
                 if bits > 64 {
                     let f = self.sdivmod(bits, runtime);
@@ -394,191 +406,218 @@ impl<'a> Contract<'a> {
                     let rem = self.builder.build_alloca(left.get_type(), "");
 
                     self.builder
-                        .build_call(f, &[left.into(), right.into(), rem.into()], "sdiv");
+                        .build_call(f, &[left, right, rem.into()], "sdiv");
 
-                    self.builder.build_load(rem, "srem").into_int_value()
+                    self.builder.build_load(rem, "srem")
                 } else {
-                    self.builder.build_int_signed_rem(left, right, "")
+                    self.builder
+                        .build_int_signed_rem(left.into_int_value(), right.into_int_value(), "")
+                        .into()
                 }
             }
             cfg::Expression::Power(l, r) => {
                 let left = self.expression(l, vartab, runtime);
                 let right = self.expression(r, vartab, runtime);
 
-                let bits = left.get_type().get_bit_width();
+                let bits = left.into_int_value().get_type().get_bit_width();
 
                 let f = self.upower(bits);
 
                 self.builder
-                    .build_call(f, &[left.into(), right.into()], "power")
+                    .build_call(f, &[left, right], "power")
                     .try_as_basic_value()
                     .left()
                     .unwrap()
-                    .into_int_value()
             }
             cfg::Expression::Equal(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::EQ, left, right, "")
+                    .into()
             }
             cfg::Expression::NotEqual(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::NE, left, right, "")
+                    .into()
             }
             cfg::Expression::SMore(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::SGT, left, right, "")
+                    .into()
             }
             cfg::Expression::SMoreEqual(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::SGE, left, right, "")
+                    .into()
             }
             cfg::Expression::SLess(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::SLT, left, right, "")
+                    .into()
             }
             cfg::Expression::SLessEqual(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::SLE, left, right, "")
+                    .into()
             }
             cfg::Expression::UMore(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::UGT, left, right, "")
+                    .into()
             }
             cfg::Expression::UMoreEqual(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::UGE, left, right, "")
+                    .into()
             }
             cfg::Expression::ULess(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::ULT, left, right, "")
+                    .into()
             }
             cfg::Expression::ULessEqual(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::ULE, left, right, "")
+                    .into()
             }
             cfg::Expression::Variable(_, s) => {
                 if vartab[*s].stack {
                     self.builder
                         .build_load(vartab[*s].value.into_pointer_value(), "")
-                        .into_int_value()
                 } else {
-                    vartab[*s].value.into_int_value()
+                    vartab[*s].value
                 }
             }
             cfg::Expression::ZeroExt(t, e) => {
-                let e = self.expression(e, vartab, runtime);
+                let e = self.expression(e, vartab, runtime).into_int_value();
                 let ty = t.LLVMType(self.ns, &self.context);
 
-                self.builder.build_int_z_extend(e, ty, "")
+                self.builder
+                    .build_int_z_extend(e, ty.into_int_type(), "")
+                    .into()
             }
             cfg::Expression::UnaryMinus(e) => {
-                let e = self.expression(e, vartab, runtime);
+                let e = self.expression(e, vartab, runtime).into_int_value();
 
-                self.builder.build_int_neg(e, "")
+                self.builder.build_int_neg(e, "").into()
             }
             cfg::Expression::SignExt(t, e) => {
-                let e = self.expression(e, vartab, runtime);
+                let e = self.expression(e, vartab, runtime).into_int_value();
                 let ty = t.LLVMType(self.ns, &self.context);
 
-                self.builder.build_int_s_extend(e, ty, "")
+                self.builder
+                    .build_int_s_extend(e, ty.into_int_type(), "")
+                    .into()
             }
             cfg::Expression::Trunc(t, e) => {
-                let e = self.expression(e, vartab, runtime);
+                let e = self.expression(e, vartab, runtime).into_int_value();
                 let ty = t.LLVMType(self.ns, &self.context);
 
-                self.builder.build_int_truncate(e, ty, "")
+                self.builder
+                    .build_int_truncate(e, ty.into_int_type(), "")
+                    .into()
             }
             cfg::Expression::Not(e) => {
-                let e = self.expression(e, vartab, runtime);
+                let e = self.expression(e, vartab, runtime).into_int_value();
 
                 self.builder
                     .build_int_compare(IntPredicate::EQ, e, e.get_type().const_zero(), "")
+                    .into()
             }
             cfg::Expression::Complement(e) => {
-                let e = self.expression(e, vartab, runtime);
+                let e = self.expression(e, vartab, runtime).into_int_value();
 
-                self.builder.build_not(e, "")
+                self.builder.build_not(e, "").into()
             }
             cfg::Expression::Or(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_or(left, right, "")
+                self.builder.build_or(left, right, "").into()
             }
             cfg::Expression::And(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_and(left, right, "")
+                self.builder.build_and(left, right, "").into()
             }
             cfg::Expression::BitwiseOr(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_or(left, right, "")
+                self.builder.build_or(left, right, "").into()
             }
             cfg::Expression::BitwiseAnd(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_and(left, right, "")
+                self.builder.build_and(left, right, "").into()
             }
             cfg::Expression::BitwiseXor(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_xor(left, right, "")
+                self.builder.build_xor(left, right, "").into()
             }
             cfg::Expression::ShiftLeft(l, r) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_left_shift(left, right, "")
+                self.builder.build_left_shift(left, right, "").into()
             }
             cfg::Expression::ShiftRight(l, r, signed) => {
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder.build_right_shift(left, right, *signed, "")
+                self.builder
+                    .build_right_shift(left, right, *signed, "")
+                    .into()
+            }
+            cfg::Expression::IndexAccess(a, i) => {
+                let array = self.expression(a, vartab, runtime).into_pointer_value();
+                let index = self.expression(i, vartab, runtime).into_int_value();
+
+                unsafe {
+                    self.builder
+                        .build_gep(array, &[index], "index_access")
+                        .into()
+                }
             }
             cfg::Expression::Ternary(c, l, r) => {
-                let cond = self.expression(c, vartab, runtime);
-                let left = self.expression(l, vartab, runtime);
-                let right = self.expression(r, vartab, runtime);
+                let cond = self.expression(c, vartab, runtime).into_int_value();
+                let left = self.expression(l, vartab, runtime).into_int_value();
+                let right = self.expression(r, vartab, runtime).into_int_value();
 
-                self.builder
-                    .build_select(cond, left, right, "")
-                    .into_int_value()
+                self.builder.build_select(cond, left, right, "")
             }
             cfg::Expression::Poison => unreachable!(),
         }
@@ -609,7 +648,7 @@ impl<'a> Contract<'a> {
             args.push(if p.ty.stack_based() {
                 ty.ptr_type(AddressSpace::Generic).into()
             } else {
-                ty.into()
+                ty
             });
         }
 
@@ -617,6 +656,7 @@ impl<'a> Contract<'a> {
             f.returns[0]
                 .ty
                 .LLVMType(self.ns, &self.context)
+                .into_int_type()
                 .fn_type(&args, false)
         } else {
             // add return values
@@ -752,7 +792,7 @@ impl<'a> Contract<'a> {
                             self.builder
                                 .build_store(w.vars[*res].value.into_pointer_value(), value_ref);
                         } else {
-                            w.vars[*res].value = value_ref.into();
+                            w.vars[*res].value = value_ref;
                         }
                     }
                     cfg::Instr::Constant { res, constant } => {
@@ -762,7 +802,7 @@ impl<'a> Contract<'a> {
                             self.builder
                                 .build_store(w.vars[*res].value.into_pointer_value(), value_ref);
                         } else {
-                            w.vars[*res].value = value_ref.into();
+                            w.vars[*res].value = value_ref;
                         }
                     }
                     cfg::Instr::Branch { bb: dest } => {
@@ -837,8 +877,11 @@ impl<'a> Contract<'a> {
                         };
 
                         self.builder.position_at_end(&pos);
-                        self.builder
-                            .build_conditional_branch(cond, &bb_true, &bb_false);
+                        self.builder.build_conditional_branch(
+                            cond.into_int_value(),
+                            &bb_true,
+                            &bb_false,
+                        );
                     }
                     cfg::Instr::GetStorage { local, storage } => {
                         let dest = w.vars[*local].value.into_pointer_value();
@@ -871,7 +914,7 @@ impl<'a> Contract<'a> {
 
                                 m.into()
                             } else {
-                                val.into()
+                                val
                             });
                         }
 
@@ -1683,11 +1726,23 @@ impl ast::PrimitiveType {
 
 impl resolver::Type {
     #[allow(non_snake_case)]
-    fn LLVMType<'a>(&self, ns: &resolver::Contract, context: &'a Context) -> IntType<'a> {
+    fn LLVMType<'a>(&self, ns: &resolver::Contract, context: &'a Context) -> BasicTypeEnum<'a> {
         match self {
-            resolver::Type::Primitive(e) => e.LLVMType(context),
-            resolver::Type::Enum(n) => ns.enums[*n].ty.LLVMType(context),
-            resolver::Type::FixedArray(_, _) => unimplemented!(),
+            resolver::Type::Primitive(e) => BasicTypeEnum::IntType(e.LLVMType(context)),
+            resolver::Type::Enum(n) => BasicTypeEnum::IntType(ns.enums[*n].ty.LLVMType(context)),
+            resolver::Type::FixedArray(base_ty, dims) => {
+                let ty = base_ty.LLVMType(ns, context).into_int_type();
+
+                let mut dims = dims.iter();
+
+                let mut aty = ty.array_type(dims.next().unwrap().to_u32().unwrap());
+
+                for dim in dims {
+                    aty = ty.array_type(dim.to_u32().unwrap());
+                }
+
+                BasicTypeEnum::ArrayType(aty)
+            }
             resolver::Type::Noreturn => unreachable!(),
         }
     }

+ 46 - 7
src/parser/ast.rs

@@ -1,3 +1,4 @@
+use super::lexer::LexicalError;
 use num_bigint::BigInt;
 use std::fmt;
 
@@ -31,12 +32,6 @@ pub enum PrimitiveType {
     DynamicBytes,
 }
 
-#[derive(Debug, PartialEq, Clone)]
-pub enum Type {
-    Primitive(PrimitiveType),
-    Unresolved(Identifier),
-}
-
 impl PrimitiveType {
     pub fn signed(self) -> bool {
         match self {
@@ -79,6 +74,12 @@ impl fmt::Display for PrimitiveType {
     }
 }
 
+#[derive(Debug, PartialEq, Clone)]
+pub enum Type {
+    Primitive(PrimitiveType, Vec<Option<(Loc, BigInt)>>),
+    Unresolved(Identifier, Vec<Option<(Loc, BigInt)>>),
+}
+
 #[derive(Debug, PartialEq)]
 pub enum StorageLocation {
     Default,
@@ -181,7 +182,7 @@ pub struct HexLiteral {
 pub enum Expression {
     PostIncrement(Loc, Box<Expression>),
     PostDecrement(Loc, Box<Expression>),
-    New(Loc, PrimitiveType),
+    New(Loc, Type),
     IndexAccess(Loc, Box<Expression>, Option<Box<Expression>>),
     MemberAccess(Loc, Identifier, Identifier),
     FunctionCall(Loc, Type, Vec<Expression>),
@@ -229,6 +230,7 @@ pub enum Expression {
     StringLiteral(Vec<StringLiteral>),
     HexLiteral(Vec<HexLiteral>),
     Variable(Identifier),
+    ArrayLiteral(Loc, Vec<Expression>),
 }
 
 impl Expression {
@@ -281,6 +283,7 @@ impl Expression {
             | Expression::BoolLiteral(loc, _)
             | Expression::NumberLiteral(loc, _)
             | Expression::AddressLiteral(loc, _)
+            | Expression::ArrayLiteral(loc, _)
             | Expression::Variable(Identifier { loc, .. }) => *loc,
             Expression::StringLiteral(v) => v[0].loc,
             Expression::HexLiteral(v) => v[0].loc,
@@ -398,3 +401,39 @@ impl Statement {
         Loc(0, 0)
     }
 }
+
+// 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(expr: Expression) -> Result<Type, LexicalError> {
+    let mut dimensions = Vec::new();
+
+    let mut expr = expr;
+
+    loop {
+        expr = match expr {
+            Expression::IndexAccess(_, r, None) => {
+                dimensions.push(None);
+
+                *r
+            }
+            Expression::IndexAccess(_, r, Some(index)) => {
+                let loc = index.loc();
+                dimensions.push(match *index {
+                    Expression::NumberLiteral(_, n) => Some((loc, n)),
+                    _ => {
+                        return Err(LexicalError::UnexpectedExpressionArrayDimension(
+                            loc.0, loc.1,
+                        ))
+                    }
+                });
+                *r
+            }
+            Expression::Variable(id) => return Ok(Type::Unresolved(id, dimensions)),
+            _ => {
+                let loc = expr.loc();
+                return Err(LexicalError::NonIdentifierInTypeName(loc.0, loc.1));
+            }
+        }
+    }
+}

+ 8 - 0
src/parser/lexer.rs

@@ -261,6 +261,8 @@ pub enum LexicalError {
     InvalidCharacterInHexLiteral(usize, char),
     UnrecognisedToken(usize, usize, String),
     PragmaMissingSemiColon(usize, usize),
+    UnexpectedExpressionArrayDimension(usize, usize),
+    NonIdentifierInTypeName(usize, usize),
 }
 
 impl fmt::Display for LexicalError {
@@ -281,6 +283,10 @@ impl fmt::Display for LexicalError {
             LexicalError::PragmaMissingSemiColon(_, _) => {
                 write!(f, "pragma is missing terminating ‘;’")
             }
+            LexicalError::UnexpectedExpressionArrayDimension(_, _) => {
+                write!(f, "array size should be a constant number")
+            }
+            LexicalError::NonIdentifierInTypeName(_, _) => write!(f, "expecting type name"),
         }
     }
 }
@@ -295,6 +301,8 @@ impl LexicalError {
             LexicalError::InvalidCharacterInHexLiteral(pos, _) => Loc(*pos, *pos),
             LexicalError::UnrecognisedToken(start, end, _) => Loc(*start, *end),
             LexicalError::PragmaMissingSemiColon(start, end) => Loc(*start, *end),
+            LexicalError::UnexpectedExpressionArrayDimension(start, end) => Loc(*start, *end),
+            LexicalError::NonIdentifierInTypeName(start, end) => Loc(*start, *end),
         }
     }
 }

+ 6 - 6
src/parser/mod.rs

@@ -97,7 +97,7 @@ mod test {
                         },
                         fields: vec![
                             VariableDeclaration {
-                                typ: Type::Primitive(PrimitiveType::Bool),
+                                typ: Type::Primitive(PrimitiveType::Bool, Vec::new()),
                                 storage: StorageLocation::Default,
                                 name: Identifier {
                                     loc: Loc(86, 92),
@@ -105,7 +105,7 @@ mod test {
                                 },
                             },
                             VariableDeclaration {
-                                typ: Type::Primitive(PrimitiveType::Uint(256)),
+                                typ: Type::Primitive(PrimitiveType::Uint(256), Vec::new()),
                                 storage: StorageLocation::Default,
                                 name: Identifier {
                                     loc: Loc(123, 129),
@@ -113,7 +113,7 @@ mod test {
                                 },
                             },
                             VariableDeclaration {
-                                typ: Type::Primitive(PrimitiveType::Bytes(2)),
+                                typ: Type::Primitive(PrimitiveType::Bytes(2), Vec::new()),
                                 storage: StorageLocation::Default,
                                 name: Identifier {
                                     loc: Loc(162, 169),
@@ -121,7 +121,7 @@ mod test {
                                 },
                             },
                             VariableDeclaration {
-                                typ: Type::Primitive(PrimitiveType::Bytes(32)),
+                                typ: Type::Primitive(PrimitiveType::Bytes(32), Vec::new()),
                                 storage: StorageLocation::Default,
                                 name: Identifier {
                                     loc: Loc(203, 209),
@@ -133,7 +133,7 @@ mod test {
                     ContractPart::ContractVariableDefinition(Box::new(
                         ContractVariableDefinition {
                             doc: vec![],
-                            ty: Type::Primitive(PrimitiveType::String),
+                            ty: Type::Primitive(PrimitiveType::String, Vec::new()),
                             attrs: vec![],
                             name: Identifier {
                                 loc: Loc(260, 268),
@@ -146,7 +146,7 @@ mod test {
                     ContractPart::ContractVariableDefinition(Box::new(
                         ContractVariableDefinition {
                             doc: vec![],
-                            ty: Type::Primitive(PrimitiveType::Int(64)),
+                            ty: Type::Primitive(PrimitiveType::Int(64), Vec::new()),
                             attrs: vec![],
                             name: Identifier {
                                 loc: Loc(296, 306),

+ 23 - 7
src/parser/solidity.lalrpop

@@ -5,6 +5,7 @@ use num_traits::Num;
 use parser::ast::*;
 use parser::box_option;
 use super::lexer::{Token, LexicalError, fold_doc_comments, CommentType};
+use lalrpop_util::ParseError;
 
 grammar<'input>(input: &'input str);
 
@@ -40,9 +41,15 @@ PrimitiveType: PrimitiveType = {
     Bytes => PrimitiveType::Bytes(<>),
 }
 
+ArrayDimension: Option<(Loc, BigInt)> = {
+    "[" "]" => None,
+    "[" <l:@L> <n:LexNumber> <r:@R> "]" => Some((Loc(l, r), BigInt::from_str(n).unwrap()))
+}
+
 Type: Type = {
-    PrimitiveType => Type::Primitive(<>),
-    Identifier => Type::Unresolved(<>),
+    <p:PrimitiveType> => Type::Primitive(p, Vec::new()),
+    <p:PrimitiveType> <d:ArrayDimension+> => Type::Primitive(p, d),
+    Precedence0 =>? expr_to_type(<>).map_err(|e| ParseError::User { error: e }),
 }
 
 StorageLocation: StorageLocation = {
@@ -230,14 +237,10 @@ Precedence2: Expression = {
     <a:@L> "+" <b:@R> <e:Precedence2> => Expression::UnaryPlus(Loc(a, b), Box::new(e)),
     <a:@L> "-" <b:@R> <e:Precedence2> => Expression::UnaryMinus(Loc(a, b), Box::new(e)),
     Precedence1,
+    Precedence0,
 }
 
 Precedence1: Expression = {
-    <a:@L> <e:Precedence1> "++" <b:@R> => Expression::PostIncrement(Loc(a, b), Box::new(e)),
-    <a:@L> <e:Precedence1> "--" <b:@R> => Expression::PostDecrement(Loc(a, b), Box::new(e)),
-    <a:@L> "new" <t:PrimitiveType> <b:@R> => Expression::New(Loc(a, b), t),
-    <a:@L> <e:Precedence1> "[" <i:Expression?> "]" <b:@R> => Expression::IndexAccess(Loc(a, b), Box::new(e), box_option(i)),
-    <a:@L> <n:Identifier> "." <i:Identifier> <b:@R> => Expression::MemberAccess(Loc(a, b), n, i),
     <a:@L> <i:Type> "(" ")" <b:@R> => {
         Expression::FunctionCall(Loc(a, b), i, Vec::new())
     },
@@ -246,10 +249,23 @@ Precedence1: Expression = {
         v.push(e);
         Expression::FunctionCall(Loc(a, b), i, v)
     },
+}
+
+Precedence0: Expression = {
+    <a:@L> <e:Precedence0> "++" <b:@R> => Expression::PostIncrement(Loc(a, b), Box::new(e)),
+    <a:@L> <e:Precedence0> "--" <b:@R> => Expression::PostDecrement(Loc(a, b), Box::new(e)),
+    <a:@L> "new" <t:Type> <b:@R> "(" ")" => Expression::New(Loc(a, b), t),
+    <a:@L> <e:Precedence0> "[" <i:Expression?> "]" <b:@R> => Expression::IndexAccess(Loc(a, b), Box::new(e), box_option(i)),
+    <a:@L> <n:Identifier> "." <i:Identifier> <b:@R> => Expression::MemberAccess(Loc(a, b), n, i),
     <a:@L> "true" <b:@R> => Expression::BoolLiteral(Loc(a, b), true),
     <a:@L> "false" <b:@R> => Expression::BoolLiteral(Loc(a, b), false),
     <StringLiteral+> => Expression::StringLiteral(<>),
     <HexLiteral+> => Expression::HexLiteral(<>),
+    <a:@L> "[" <v:(<Precedence0> ",")*> <e:Precedence0> "]" <b:@R> => {
+        let mut v = v;
+        v.push(e);
+        Expression::ArrayLiteral(Loc(a, b), v)
+    },
     <i:Identifier> => Expression::Variable(i),
     "(" <e:Expression> ")" => e,
     <l:@L> <n:LexNumber> <r:@R> => {

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 486 - 446
src/parser/solidity.rs


+ 164 - 89
src/resolver/cfg.rs

@@ -3,6 +3,7 @@ use num_bigint::Sign;
 use num_traits::FromPrimitive;
 use num_traits::Num;
 use num_traits::One;
+use num_traits::Zero;
 use std::cmp;
 use std::cmp::Ordering;
 use std::collections::HashMap;
@@ -56,6 +57,7 @@ pub enum Expression {
     UnaryMinus(Box<Expression>),
 
     Ternary(Box<Expression>, Box<Expression>, Box<Expression>),
+    IndexAccess(Box<Expression>, Box<Expression>),
 
     Or(Box<Expression>, Box<Expression>),
     And(Box<Expression>, Box<Expression>),
@@ -607,7 +609,15 @@ fn statement(
 ) -> Result<bool, ()> {
     match stmt {
         ast::Statement::VariableDefinition(decl, init) => {
-            let var_ty = ns.resolve_type(&decl.typ, errors)?;
+            let var_ty = ns.resolve_type(&decl.typ, Some(errors))?;
+
+            if var_ty.size_hint() > BigInt::from(1024 * 1024) {
+                errors.push(Output::error(
+                    stmt.loc(),
+                    "type to large to fit into memory".to_string(),
+                ));
+                return Err(());
+            }
 
             let e_t = if let Some(init) = init {
                 let (expr, init_ty) = expression(init, cfg, ns, &mut Some(vartab), errors)?;
@@ -1209,6 +1219,39 @@ fn coerce_int(
     ))
 }
 
+/// Try to convert a BigInt into a Expression::NumberLiteral. This checks for sign,
+/// width and creates to correct Type.
+fn bigint_to_expression(
+    loc: &ast::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(int_size, n.clone()),
+                resolver::Type::Primitive(ast::PrimitiveType::Int(int_size)),
+            ))
+        }
+    } else if bits > 256 {
+        errors.push(Output::error(*loc, format!("{} is too large", n)));
+        Err(())
+    } else {
+        Ok((
+            Expression::NumberLiteral(int_size, n.clone()),
+            resolver::Type::Primitive(ast::PrimitiveType::Uint(int_size)),
+        ))
+    }
+}
+
 pub fn cast(
     loc: &ast::Loc,
     expr: Expression,
@@ -1737,32 +1780,7 @@ pub fn expression(
                 resolver::Type::Primitive(ast::PrimitiveType::Bytes(length as u8)),
             ))
         }
-        ast::Expression::NumberLiteral(loc, b) => {
-            // Return smallest type
-            let bits = b.bits();
-
-            let int_size = if bits < 7 { 8 } else { (bits + 7) & !7 } as u16;
-
-            if b.sign() == Sign::Minus {
-                if bits > 255 {
-                    errors.push(Output::error(*loc, format!("{} is too large", b)));
-                    Err(())
-                } else {
-                    Ok((
-                        Expression::NumberLiteral(int_size, b.clone()),
-                        resolver::Type::Primitive(ast::PrimitiveType::Int(int_size)),
-                    ))
-                }
-            } else if bits > 256 {
-                errors.push(Output::error(*loc, format!("{} is too large", b)));
-                Err(())
-            } else {
-                Ok((
-                    Expression::NumberLiteral(int_size, b.clone()),
-                    resolver::Type::Primitive(ast::PrimitiveType::Uint(int_size)),
-                ))
-            }
-        }
+        ast::Expression::NumberLiteral(loc, b) => bigint_to_expression(loc, b, errors),
         ast::Expression::AddressLiteral(loc, n) => {
             let address = to_hexstr_eip55(n);
 
@@ -2583,12 +2601,9 @@ pub fn expression(
             Ok((Expression::Variable(id.loc, pos), ty))
         }
         ast::Expression::FunctionCall(loc, ty, args) => {
-            let to = match ty {
-                ast::Type::Primitive(e) => Some(resolver::Type::Primitive(*e)),
-                ast::Type::Unresolved(s) => match ns.resolve_enum(s) {
-                    Some(v) => Some(resolver::Type::Enum(v)),
-                    None => None,
-                },
+            let to = match ns.resolve_type(ty, None) {
+                Ok(ty) => Some(ty),
+                Err(_) => None,
             };
 
             // Cast
@@ -2609,7 +2624,7 @@ pub fn expression(
                 };
             }
 
-            let funcs = if let ast::Type::Unresolved(s) = ty {
+            let funcs = if let ast::Type::Unresolved(s, _) = ty {
                 ns.resolve_func(s, errors)?
             } else {
                 unreachable!();
@@ -2758,18 +2773,20 @@ pub fn expression(
 
             get_contract_storage(&var, cfg, tab);
 
-            let array_length =
-                if let resolver::Type::Primitive(ast::PrimitiveType::Bytes(n)) = var.ty {
-                    n
-                } else {
+            let array_length = match var.ty {
+                resolver::Type::Primitive(ast::PrimitiveType::Bytes(n)) => BigInt::from(n),
+                resolver::Type::FixedArray(_, _) => var.ty.array_length().clone(),
+                _ => {
                     errors.push(Output::error(
                         *loc,
-                        format!("variable {} is not an array", id.name),
+                        format!("variable {} is not an array", id.name),
                     ));
                     return Err(());
-                };
+                }
+            };
 
             let (index_width, _) = get_int_length(&index_type, &index.loc(), false, ns, errors)?;
+            let array_width = array_length.bits();
 
             let pos = tab.temp(
                 &ast::Identifier {
@@ -2790,57 +2807,107 @@ pub fn expression(
             let out_of_range = cfg.new_basic_block("out_of_range".to_string());
             let in_range = cfg.new_basic_block("in_range".to_string());
 
-            // We're cheating a bit. Any signed value < 0 will also be caught by doing a unsigned compare
-            cfg.add(
-                tab,
-                Instr::BranchCond {
-                    cond: Expression::UMoreEqual(
-                        Box::new(Expression::Variable(index.loc(), pos)),
-                        Box::new(Expression::NumberLiteral(
-                            index_width,
-                            BigInt::from_u8(array_length).unwrap(),
-                        )),
-                    ),
-                    true_: out_of_range,
-                    false_: in_range,
-                },
-            );
+            if index_type.signed() {
+                // first check that our index is not negative
+                let positive = cfg.new_basic_block("positive".to_string());
+
+                cfg.add(
+                    tab,
+                    Instr::BranchCond {
+                        cond: Expression::SLess(
+                            Box::new(Expression::Variable(index.loc(), pos)),
+                            Box::new(Expression::NumberLiteral(index_width, BigInt::zero())),
+                        ),
+                        true_: out_of_range,
+                        false_: positive,
+                    },
+                );
+
+                cfg.set_basic_block(positive);
+
+                // If the index if of less bits than the array length, don't bother checking
+                if index_width as usize >= array_width {
+                    cfg.add(
+                        tab,
+                        Instr::BranchCond {
+                            cond: Expression::SMoreEqual(
+                                Box::new(Expression::Variable(index.loc(), pos)),
+                                Box::new(Expression::NumberLiteral(index_width, array_length)),
+                            ),
+                            true_: out_of_range,
+                            false_: in_range,
+                        },
+                    );
+                } else {
+                    cfg.add(tab, Instr::Branch { bb: in_range });
+                }
+            } else if index_width as usize <= array_width {
+                cfg.add(
+                    tab,
+                    Instr::BranchCond {
+                        cond: Expression::UMoreEqual(
+                            Box::new(Expression::Variable(index.loc(), pos)),
+                            Box::new(Expression::NumberLiteral(index_width, array_length)),
+                        ),
+                        true_: out_of_range,
+                        false_: in_range,
+                    },
+                );
+            } else {
+                // if the index is less bits than the array, it is always in range
+                cfg.add(tab, Instr::Branch { bb: in_range });
+            }
 
             cfg.set_basic_block(out_of_range);
             cfg.add(tab, Instr::AssertFailure {});
 
             cfg.set_basic_block(in_range);
 
-            let res_ty = resolver::Type::Primitive(ast::PrimitiveType::Bytes(1));
+            match var.ty {
+                resolver::Type::Primitive(ast::PrimitiveType::Bytes(array_length)) => {
+                    let res_ty = resolver::Type::Primitive(ast::PrimitiveType::Bytes(1));
 
-            Ok((
-                Expression::Trunc(
-                    res_ty.clone(),
-                    Box::new(Expression::ShiftRight(
-                        Box::new(Expression::Variable(*loc, var.pos)),
-                        // shift by (array_length - 1 - index) * 8
-                        Box::new(Expression::ShiftLeft(
-                            Box::new(Expression::Subtract(
-                                Box::new(Expression::NumberLiteral(
-                                    array_length as u16 * 8,
-                                    BigInt::from_u8(array_length - 1).unwrap(),
-                                )),
-                                Box::new(cast_shift_arg(
-                                    Expression::Variable(index.loc(), pos),
-                                    index_width,
-                                    &var.ty,
+                    Ok((
+                        Expression::Trunc(
+                            res_ty.clone(),
+                            Box::new(Expression::ShiftRight(
+                                Box::new(Expression::Variable(*loc, var.pos)),
+                                // shift by (array_length - 1 - index) * 8
+                                Box::new(Expression::ShiftLeft(
+                                    Box::new(Expression::Subtract(
+                                        Box::new(Expression::NumberLiteral(
+                                            array_length as u16 * 8,
+                                            BigInt::from_u8(array_length - 1).unwrap(),
+                                        )),
+                                        Box::new(cast_shift_arg(
+                                            Expression::Variable(index.loc(), pos),
+                                            index_width,
+                                            &var.ty,
+                                        )),
+                                    )),
+                                    Box::new(Expression::NumberLiteral(
+                                        array_length as u16 * 8,
+                                        BigInt::from_u8(3).unwrap(),
+                                    )),
                                 )),
+                                false,
                             )),
-                            Box::new(Expression::NumberLiteral(
-                                array_length as u16 * 8,
-                                BigInt::from_u8(3).unwrap(),
-                            )),
-                        )),
-                        false,
-                    )),
-                ),
-                res_ty,
-            ))
+                        ),
+                        res_ty,
+                    ))
+                }
+                resolver::Type::FixedArray(_, _) => Ok((
+                    Expression::IndexAccess(
+                        Box::new(Expression::Variable(id.loc, var.pos)),
+                        Box::new(Expression::Variable(*loc, pos)),
+                    ),
+                    var.ty.deref(),
+                )),
+                _ => {
+                    // should not happen as type-checking already done
+                    unreachable!();
+                }
+            }
         }
         ast::Expression::MemberAccess(loc, namespace, id) => {
             // Is it an enum
@@ -2863,17 +2930,25 @@ pub fn expression(
                 };
             }
 
-            // is it an bytesN.length
+            // is it an bytesN.length / array.length
             if let Some(ref mut tab) = *vartab {
                 let var = tab.find(namespace, ns, errors)?;
 
-                if let resolver::Type::Primitive(ast::PrimitiveType::Bytes(n)) = var.ty {
-                    if id.name == "length" {
-                        return Ok((
-                            Expression::NumberLiteral(8, BigInt::from_u8(n).unwrap()),
-                            resolver::Type::Primitive(ast::PrimitiveType::Uint(8)),
-                        ));
+                match var.ty {
+                    resolver::Type::Primitive(ast::PrimitiveType::Bytes(n)) => {
+                        if id.name == "length" {
+                            return Ok((
+                                Expression::NumberLiteral(8, BigInt::from_u8(n).unwrap()),
+                                resolver::Type::Primitive(ast::PrimitiveType::Uint(8)),
+                            ));
+                        }
+                    }
+                    resolver::Type::FixedArray(_, dim) => {
+                        if id.name == "length" {
+                            return bigint_to_expression(loc, dim.last().unwrap(), errors);
+                        }
                     }
+                    _ => (),
                 }
             }
 

+ 2 - 2
src/resolver/functions.rs

@@ -40,7 +40,7 @@ pub fn function_decl(
     }
 
     for p in &f.params {
-        match ns.resolve_type(&p.typ, errors) {
+        match ns.resolve_type(&p.typ, Some(errors)) {
             Ok(s) => params.push(Parameter {
                 name: p
                     .name
@@ -53,7 +53,7 @@ pub fn function_decl(
     }
 
     for r in &f.returns {
-        match ns.resolve_type(&r.typ, errors) {
+        match ns.resolve_type(&r.typ, Some(errors)) {
             Ok(s) => returns.push(Parameter {
                 name: r
                     .name

+ 117 - 13
src/resolver/mod.rs

@@ -1,5 +1,8 @@
 use abi;
 use emit;
+use num_bigint::BigInt;
+use num_traits::Signed;
+use num_traits::Zero;
 use output::{Note, Output};
 use parser::ast;
 use std::collections::HashMap;
@@ -18,7 +21,7 @@ use resolver::cfg::{ControlFlowGraph, Instr, Vartable};
 #[derive(PartialEq, Clone, Debug)]
 pub enum Type {
     Primitive(ast::PrimitiveType),
-    FixedArray(Box<Type>, Vec<usize>),
+    FixedArray(Box<Type>, Vec<BigInt>),
     Enum(usize),
     Noreturn,
 }
@@ -50,6 +53,50 @@ impl Type {
         }
     }
 
+    /// Give the type of an array after dereference. This can only be used on
+    /// array types and will cause a panic otherwise.
+    pub fn deref(&self) -> Self {
+        match self {
+            Type::FixedArray(ty, dim) if dim.len() > 1 => {
+                Type::FixedArray(ty.clone(), dim[..dim.len() - 1].to_vec())
+            }
+            Type::FixedArray(ty, dim) if dim.len() == 1 => *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) -> &BigInt {
+        match self {
+            Type::FixedArray(_, dim) => dim.last().unwrap(),
+            _ => 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) -> BigInt {
+        match self {
+            Type::Enum(_) => BigInt::from(1),
+            Type::Primitive(ast::PrimitiveType::Bool) => BigInt::from(1),
+            Type::Primitive(ast::PrimitiveType::Address) => BigInt::from(20),
+            Type::Primitive(ast::PrimitiveType::Bytes(n)) => BigInt::from(*n),
+            Type::Primitive(ast::PrimitiveType::Uint(n))
+            | Type::Primitive(ast::PrimitiveType::Int(n)) => BigInt::from(n / 8),
+            Type::FixedArray(ty, dims) => {
+                let mut size = ty.size_hint();
+
+                for dim in dims {
+                    size *= dim;
+                }
+                size
+            }
+            _ => unimplemented!(),
+        }
+    }
+
     pub fn bits(&self) -> u16 {
         match self {
             Type::Primitive(e) => e.bits(),
@@ -282,26 +329,83 @@ impl Contract {
         true
     }
 
-    pub fn resolve_type(&self, id: &ast::Type, errors: &mut Vec<Output>) -> Result<Type, ()> {
+    /// Resolve the parsed data type. The type can be a primitive, enum and also an arrays.
+    pub fn resolve_type(
+        &self,
+        id: &ast::Type,
+        errors: Option<&mut Vec<Output>>,
+    ) -> Result<Type, ()> {
         match id {
-            ast::Type::Primitive(e) => Ok(Type::Primitive(*e)),
-            ast::Type::Unresolved(s) => match self.symbols.get(&s.name) {
+            ast::Type::Primitive(p, dimensions) if dimensions.is_empty() => Ok(Type::Primitive(*p)),
+            ast::Type::Primitive(p, dimensions) => {
+                // resolve
+
+                let mut fixed = true;
+                let mut fixed_dimensions = Vec::new();
+
+                for d in dimensions.iter() {
+                    if let Some((loc, n)) = d {
+                        if n.is_zero() {
+                            if let Some(errors) = errors {
+                                errors.push(Output::decl_error(
+                                    *loc,
+                                    "zero size of array declared".to_string(),
+                                ));
+                            }
+                            return Err(());
+                        } else if n.is_negative() {
+                            if let Some(errors) = errors {
+                                errors.push(Output::decl_error(
+                                    *loc,
+                                    "negative size of array declared".to_string(),
+                                ));
+                            }
+                            return Err(());
+                        }
+                        fixed_dimensions.push(n.clone());
+                    } else {
+                        fixed = false;
+                    }
+                }
+
+                if fixed {
+                    Ok(Type::FixedArray(
+                        Box::new(Type::Primitive(*p)),
+                        fixed_dimensions,
+                    ))
+                } else {
+                    unimplemented!();
+                }
+            }
+            ast::Type::Unresolved(id, _) => match self.symbols.get(&id.name) {
                 None => {
-                    errors.push(Output::decl_error(
-                        s.loc,
-                        format!("`{}' is not declared", s.name),
-                    ));
+                    if let Some(errors) = errors {
+                        errors.push(Output::decl_error(
+                            id.loc,
+                            format!("`{}' is not declared", id.name),
+                        ));
+                    }
                     Err(())
                 }
                 Some(Symbol::Enum(_, n)) => Ok(Type::Enum(*n)),
                 Some(Symbol::Function(_)) => {
-                    errors.push(Output::decl_error(
-                        s.loc,
-                        format!("`{}' is a function", s.name),
-                    ));
+                    if let Some(errors) = errors {
+                        errors.push(Output::decl_error(
+                            id.loc,
+                            format!("`{}' is a function", id.name),
+                        ));
+                    }
+                    Err(())
+                }
+                Some(Symbol::Variable(_, _)) => {
+                    if let Some(errors) = errors {
+                        errors.push(Output::decl_error(
+                            id.loc,
+                            format!("`{}' is a contract variable", id.name),
+                        ));
+                    }
                     Err(())
                 }
-                Some(Symbol::Variable(_, n)) => Ok(self.variables[*n].ty.clone()),
             },
         }
     }

+ 1 - 1
src/resolver/variables.rs

@@ -37,7 +37,7 @@ fn var_decl(
     vartab: &mut Vartable,
     errors: &mut Vec<Output>,
 ) -> bool {
-    let ty = match ns.resolve_type(&s.ty, errors) {
+    let ty = match ns.resolve_type(&s.ty, Some(errors)) {
         Ok(s) => s,
         Err(()) => {
             return false;

+ 1 - 1
tests/loops.rs

@@ -6,7 +6,7 @@ use solang::{parse_and_resolve, Target};
 fn first_error(errors: Vec<output::Output>) -> String {
     match errors.iter().find(|m| m.level == output::Level::Error) {
         Some(m) => m.message.to_owned(),
-        None => panic!("no errors detected"),
+        None => panic!("no errors found"),
     }
 }
 

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio