Pārlūkot izejas kodu

Introduce dynamic array type

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 gadi atpakaļ
vecāks
revīzija
9792bcaadb

+ 8 - 4
src/abi/substrate.rs

@@ -412,13 +412,17 @@ fn ty_to_abi(
             }
         }
         resolver::Type::Undef => unreachable!(),
-        resolver::Type::FixedArray(ty, dims) => {
+        resolver::Type::Array(ty, dims) => {
             let mut param_ty = ty_to_abi(ty, contract, registry);
 
             for d in dims {
-                param_ty = ParamType {
-                    ty: registry.builtin_array_type(param_ty.ty, d.to_usize().unwrap()),
-                    display_name: vec![],
+                if let Some(d) = d {
+                    param_ty = ParamType {
+                        ty: registry.builtin_array_type(param_ty.ty, d.to_usize().unwrap()),
+                        display_name: vec![],
+                    }
+                } else {
+                    // FIXME:
                 }
             }
 

+ 78 - 71
src/emit/ethabiencoder.rs

@@ -36,30 +36,34 @@ impl EthAbiEncoder {
             resolver::Type::Enum(n) => {
                 self.encode_primitive(contract, contract.ns.enums[*n].ty, *data, arg);
             }
-            resolver::Type::FixedArray(_, dim) => {
-                contract.emit_static_loop_with_pointer(
-                    function,
-                    0,
-                    dim[0].to_u64().unwrap(),
-                    data,
-                    |index, data| {
-                        let mut elem = unsafe {
-                            contract.builder.build_gep(
-                                arg.into_pointer_value(),
-                                &[contract.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
-
-                        let ty = ty.array_deref();
-
-                        if ty.is_reference_type() {
-                            elem = contract.builder.build_load(elem, "").into_pointer_value();
-                        }
-
-                        self.encode_ty(contract, function, &ty, elem.into(), data);
-                    },
-                );
+            resolver::Type::Array(_, dim) => {
+                if let Some(d) = &dim[0] {
+                    contract.emit_static_loop_with_pointer(
+                        function,
+                        0,
+                        d.to_u64().unwrap(),
+                        data,
+                        |index, data| {
+                            let mut elem = unsafe {
+                                contract.builder.build_gep(
+                                    arg.into_pointer_value(),
+                                    &[contract.context.i32_type().const_zero(), index],
+                                    "index_access",
+                                )
+                            };
+
+                            let ty = ty.array_deref();
+
+                            if ty.is_reference_type() {
+                                elem = contract.builder.build_load(elem, "").into_pointer_value();
+                            }
+
+                            self.encode_ty(contract, function, &ty, elem.into(), data);
+                        },
+                    );
+                } else {
+                    // FIXME
+                }
             }
             resolver::Type::Struct(n) => {
                 for (i, field) in contract.ns.structs[*n].fields.iter().enumerate() {
@@ -380,9 +384,17 @@ impl EthAbiEncoder {
                 .iter()
                 .map(|f| self.encoded_length(&f.ty, contract))
                 .sum(),
-            resolver::Type::FixedArray(ty, dims) => {
-                self.encoded_length(ty, contract)
-                    * dims.iter().map(|d| d.to_u64().unwrap()).product::<u64>()
+            resolver::Type::Array(ty, dims) => {
+                let mut product = 1;
+
+                for dim in dims {
+                    match dim {
+                        Some(d) => product *= d.to_u64().unwrap(),
+                        None => return product,
+                    }
+                }
+
+                product * self.encoded_length(&ty, contract)
             }
             resolver::Type::Undef => unreachable!(),
             resolver::Type::Ref(r) => self.encoded_length(r, contract),
@@ -402,50 +414,47 @@ impl EthAbiEncoder {
         let pty = match &ty {
             resolver::Type::Primitive(e) => e,
             resolver::Type::Enum(n) => &contract.ns.enums[*n].ty,
-            resolver::Type::FixedArray(_, dim) => {
-                let to = to.unwrap_or_else(|| {
-                    contract
-                        .builder
-                        .build_alloca(ty.llvm_type(contract.ns, contract.context), "")
-                });
-
-                contract.emit_static_loop_with_pointer(
-                    function,
-                    0,
-                    dim[0].to_u64().unwrap(),
-                    data,
-                    |index: IntValue<'b>, data: &mut PointerValue<'b>| {
-                        let elem = unsafe {
-                            contract.builder.build_gep(
-                                to,
-                                &[contract.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
-
-                        let ty = ty.array_deref();
-
-                        if ty.is_reference_type() {
-                            let val = contract.builder.build_alloca(
-                                ty.deref().llvm_type(contract.ns, contract.context),
-                                "",
-                            );
-                            self.decode_ty(contract, function, &ty, Some(val), data);
-                            contract.builder.build_store(elem, val);
-                        } else {
-                            self.decode_ty(contract, function, &ty, Some(elem), data);
-                        }
-                    },
-                );
+            resolver::Type::Array(_, dim) => {
+                let to =
+                    to.unwrap_or_else(|| contract.builder.build_alloca(contract.llvm_type(ty), ""));
+
+                if let Some(d) = &dim[0] {
+                    contract.emit_static_loop_with_pointer(
+                        function,
+                        0,
+                        d.to_u64().unwrap(),
+                        data,
+                        |index: IntValue<'b>, data: &mut PointerValue<'b>| {
+                            let elem = unsafe {
+                                contract.builder.build_gep(
+                                    to,
+                                    &[contract.context.i32_type().const_zero(), index],
+                                    "index_access",
+                                )
+                            };
+
+                            let ty = ty.array_deref();
+
+                            if ty.is_reference_type() {
+                                let val = contract
+                                    .builder
+                                    .build_alloca(contract.llvm_type(&ty.deref()), "");
+                                self.decode_ty(contract, function, &ty, Some(val), data);
+                                contract.builder.build_store(elem, val);
+                            } else {
+                                self.decode_ty(contract, function, &ty, Some(elem), data);
+                            }
+                        },
+                    );
+                } else {
+                    // FIXME
+                }
 
                 return to.into();
             }
             resolver::Type::Struct(n) => {
-                let to = to.unwrap_or_else(|| {
-                    contract
-                        .builder
-                        .build_alloca(ty.llvm_type(contract.ns, contract.context), "")
-                });
+                let to =
+                    to.unwrap_or_else(|| contract.builder.build_alloca(contract.llvm_type(ty), ""));
 
                 for (i, field) in contract.ns.structs[*n].fields.iter().enumerate() {
                     let elem = unsafe {
@@ -462,7 +471,7 @@ impl EthAbiEncoder {
                     if field.ty.is_reference_type() {
                         let val = contract
                             .builder
-                            .build_alloca(field.ty.llvm_type(contract.ns, contract.context), "");
+                            .build_alloca(contract.llvm_type(&field.ty), "");
 
                         self.decode_ty(contract, function, &field.ty, Some(val), data);
 
@@ -609,9 +618,7 @@ impl EthAbiEncoder {
                 );
 
                 if *n <= 64 && to.is_none() {
-                    contract
-                        .builder
-                        .build_load(store, &format!("abi_int{}", *n))
+                    contract.builder.build_load(store, &format!("abi_int{}", n))
                 } else {
                     store.into()
                 }

+ 147 - 146
src/emit/mod.rs

@@ -356,9 +356,7 @@ impl<'a> Contract<'a> {
                 .into(),
             Expression::NumberLiteral(_, bits, n) => self.number_literal(*bits as u32, n).into(),
             Expression::StructLiteral(_, ty, exprs) => {
-                let s = self
-                    .builder
-                    .build_alloca(ty.llvm_type(self.ns, self.context), "struct");
+                let s = self.builder.build_alloca(self.llvm_type(ty), "struct");
 
                 for (i, f) in exprs.iter().enumerate() {
                     let elem = unsafe {
@@ -703,7 +701,7 @@ impl<'a> Contract<'a> {
             Expression::StorageLoad(_, ty, e) => {
                 let dest = self
                     .builder
-                    .build_alloca(ty.llvm_type(self.ns, &self.context), "storage_load_temp");
+                    .build_alloca(self.llvm_type(ty), "storage_load_temp");
                 // The storage slot is an i256 accessed through a pointer, so we need
                 // to store it
                 let mut slot = self
@@ -722,7 +720,7 @@ impl<'a> Contract<'a> {
                 let e = self
                     .expression(e, vartab, function, runtime)
                     .into_int_value();
-                let ty = t.llvm_type(self.ns, &self.context);
+                let ty = self.llvm_type(t);
 
                 self.builder
                     .build_int_z_extend(e, ty.into_int_type(), "")
@@ -739,7 +737,7 @@ impl<'a> Contract<'a> {
                 let e = self
                     .expression(e, vartab, function, runtime)
                     .into_int_value();
-                let ty = t.llvm_type(self.ns, &self.context);
+                let ty = self.llvm_type(t);
 
                 self.builder
                     .build_int_s_extend(e, ty.into_int_type(), "")
@@ -749,7 +747,7 @@ impl<'a> Contract<'a> {
                 let e = self
                     .expression(e, vartab, function, runtime)
                     .into_int_value();
-                let ty = t.llvm_type(self.ns, &self.context);
+                let ty = self.llvm_type(t);
 
                 self.builder
                     .build_int_truncate(e, ty.into_int_type(), "")
@@ -943,7 +941,7 @@ impl<'a> Contract<'a> {
                 // non-const array literals should alloca'ed and each element assigned
                 let array = self
                     .builder
-                    .build_alloca(ty.llvm_type(self.ns, &self.context), "array_literal");
+                    .build_alloca(self.llvm_type(ty), "array_literal");
 
                 for (i, expr) in exprs.iter().enumerate() {
                     let mut ind = vec![self.context.i32_type().const_zero()];
@@ -990,45 +988,46 @@ impl<'a> Contract<'a> {
         runtime: &dyn TargetRuntime,
     ) {
         match ty {
-            resolver::Type::FixedArray(_, dim) => {
+            resolver::Type::Array(_, dim) => {
                 let ty = ty.array_deref();
 
-                self.emit_static_loop_with_int(
-                    function,
-                    0,
-                    dim[0].to_u64().unwrap(),
-                    slot,
-                    |index: IntValue<'b>, slot: &mut IntValue<'b>| {
-                        let elem = unsafe {
-                            self.builder.build_gep(
-                                dest,
-                                &[self.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
-
-                        if ty.is_reference_type() {
-                            let ty = ty.deref();
-                            let val = self
-                                .builder
-                                .build_alloca(ty.llvm_type(self.ns, self.context), "");
+                if let Some(d) = &dim[0] {
+                    self.emit_static_loop_with_int(
+                        function,
+                        0,
+                        d.to_u64().unwrap(),
+                        slot,
+                        |index: IntValue<'b>, slot: &mut IntValue<'b>| {
+                            let elem = unsafe {
+                                self.builder.build_gep(
+                                    dest,
+                                    &[self.context.i32_type().const_zero(), index],
+                                    "index_access",
+                                )
+                            };
 
-                            self.storage_load(&ty, slot, slot_ptr, val, function, runtime);
+                            if ty.is_reference_type() {
+                                let ty = ty.deref();
+                                let val = self.builder.build_alloca(self.llvm_type(&ty), "");
+                                self.storage_load(&ty, slot, slot_ptr, val, function, runtime);
 
-                            self.builder.build_store(elem, val);
-                        } else {
-                            self.storage_load(&ty, slot, slot_ptr, elem, function, runtime);
-                        }
+                                self.builder.build_store(elem, val);
+                            } else {
+                                self.storage_load(&ty, slot, slot_ptr, elem, function, runtime);
+                            }
 
-                        if !ty.is_reference_type() {
-                            *slot = self.builder.build_int_add(
-                                *slot,
-                                self.number_literal(256, &ty.storage_slots(self.ns)),
-                                "",
-                            );
-                        }
-                    },
-                );
+                            if !ty.is_reference_type() {
+                                *slot = self.builder.build_int_add(
+                                    *slot,
+                                    self.number_literal(256, &ty.storage_slots(self.ns)),
+                                    "",
+                                );
+                            }
+                        },
+                    );
+                } else {
+                    // FIXME: iterate over dynamic array
+                }
             }
             resolver::Type::Struct(n) => {
                 for (i, field) in self.ns.structs[*n].fields.iter().enumerate() {
@@ -1044,10 +1043,9 @@ impl<'a> Contract<'a> {
                     };
 
                     if field.ty.is_reference_type() {
-                        let val = self.builder.build_alloca(
-                            field.ty.deref().llvm_type(self.ns, self.context),
-                            &field.name,
-                        );
+                        let val = self
+                            .builder
+                            .build_alloca(self.llvm_type(&field.ty.deref()), &field.name);
 
                         self.storage_load(&field.ty, slot, slot_ptr, val, function, runtime);
 
@@ -1087,38 +1085,42 @@ impl<'a> Contract<'a> {
         runtime: &dyn TargetRuntime,
     ) {
         match ty.deref() {
-            resolver::Type::FixedArray(_, dim) => {
+            resolver::Type::Array(_, dim) => {
                 let ty = ty.array_deref();
 
-                self.emit_static_loop_with_int(
-                    function,
-                    0,
-                    dim[0].to_u64().unwrap(),
-                    slot,
-                    |index: IntValue<'b>, slot: &mut IntValue<'b>| {
-                        let mut elem = unsafe {
-                            self.builder.build_gep(
-                                dest.into_pointer_value(),
-                                &[self.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
+                if let Some(d) = &dim[0] {
+                    self.emit_static_loop_with_int(
+                        function,
+                        0,
+                        d.to_u64().unwrap(),
+                        slot,
+                        |index: IntValue<'b>, slot: &mut IntValue<'b>| {
+                            let mut elem = unsafe {
+                                self.builder.build_gep(
+                                    dest.into_pointer_value(),
+                                    &[self.context.i32_type().const_zero(), index],
+                                    "index_access",
+                                )
+                            };
 
-                        if ty.is_reference_type() {
-                            elem = self.builder.build_load(elem, "").into_pointer_value();
-                        }
+                            if ty.is_reference_type() {
+                                elem = self.builder.build_load(elem, "").into_pointer_value();
+                            }
 
-                        self.storage_store(&ty, slot, slot_ptr, elem.into(), function, runtime);
+                            self.storage_store(&ty, slot, slot_ptr, elem.into(), function, runtime);
 
-                        if !ty.is_reference_type() {
-                            *slot = self.builder.build_int_add(
-                                *slot,
-                                self.number_literal(256, &ty.storage_slots(self.ns)),
-                                "",
-                            );
-                        }
-                    },
-                );
+                            if !ty.is_reference_type() {
+                                *slot = self.builder.build_int_add(
+                                    *slot,
+                                    self.number_literal(256, &ty.storage_slots(self.ns)),
+                                    "",
+                                );
+                            }
+                        },
+                    );
+                } else {
+                    // FIMXE: iterate over dynamic array
+                }
             }
             resolver::Type::Struct(n) => {
                 for (i, field) in self.ns.structs[*n].fields.iter().enumerate() {
@@ -1192,7 +1194,7 @@ impl<'a> Contract<'a> {
         let mut args: Vec<BasicTypeEnum> = Vec::new();
 
         for p in &f.params {
-            let ty = p.ty.llvm_type(self.ns, self.context);
+            let ty = self.llvm_type(&p.ty);
 
             args.push(if p.ty.stack_based() && !p.ty.is_contract_storage() {
                 ty.ptr_type(AddressSpace::Generic).into()
@@ -1202,23 +1204,19 @@ impl<'a> Contract<'a> {
         }
 
         let ftype = if f.wasm_return {
-            f.returns[0]
-                .ty
-                .llvm_type(self.ns, &self.context)
+            self.llvm_type(&f.returns[0].ty)
                 .into_int_type()
                 .fn_type(&args, false)
         } else {
             // add return values
             for p in &f.returns {
                 args.push(if p.ty.is_reference_type() && !p.ty.is_contract_storage() {
-                    p.ty.llvm_type(self.ns, &self.context)
+                    self.llvm_type(&p.ty)
                         .ptr_type(AddressSpace::Generic)
                         .ptr_type(AddressSpace::Generic)
                         .into()
                 } else {
-                    p.ty.llvm_type(self.ns, &self.context)
-                        .ptr_type(AddressSpace::Generic)
-                        .into()
+                    self.llvm_type(&p.ty).ptr_type(AddressSpace::Generic).into()
                 });
             }
             self.context.void_type().fn_type(&args, false)
@@ -1270,7 +1268,7 @@ impl<'a> Contract<'a> {
             if let Some(ref cfg_phis) = cfg_bb.phis {
                 for v in cfg_phis {
                     if !cfg.vars[*v].ty.stack_based() {
-                        let ty = cfg.vars[*v].ty.llvm_type(self.ns, &self.context);
+                        let ty = self.llvm_type(&cfg.vars[*v].ty);
 
                         phis.insert(*v, self.builder.build_phi(ty, &cfg.vars[*v].id.name));
                     }
@@ -1293,7 +1291,7 @@ impl<'a> Contract<'a> {
                     vars.push(Variable {
                         value: self
                             .builder
-                            .build_alloca(v.ty.llvm_type(self.ns, &self.context), &v.id.name)
+                            .build_alloca(self.llvm_type(&v.ty), &v.id.name)
                             .into(),
                         stack: false,
                     });
@@ -1317,7 +1315,7 @@ impl<'a> Contract<'a> {
                     vars.push(Variable {
                         value: self
                             .builder
-                            .build_alloca(v.ty.llvm_type(self.ns, &self.context), &v.id.name)
+                            .build_alloca(self.llvm_type(&v.ty), &v.id.name)
                             .into(),
                         stack: true,
                     });
@@ -1486,9 +1484,7 @@ impl<'a> Contract<'a> {
 
                             parms.push(if ty.stack_based() && !ty.is_reference_type() {
                                 // copy onto stack
-                                let m = self
-                                    .builder
-                                    .build_alloca(ty.llvm_type(self.ns, &self.context), "");
+                                let m = self.builder.build_alloca(self.llvm_type(ty), "");
 
                                 self.builder.build_store(m, val);
 
@@ -1502,10 +1498,7 @@ impl<'a> Contract<'a> {
                             for v in f.returns.iter() {
                                 parms.push(
                                     self.builder
-                                        .build_alloca(
-                                            v.ty.llvm_var(self.ns, &self.context),
-                                            &v.name,
-                                        )
+                                        .build_alloca(self.llvm_var(&v.ty), &v.name)
                                         .into(),
                                 );
                             }
@@ -1617,13 +1610,12 @@ impl<'a> Contract<'a> {
                 for v in f.returns.iter() {
                     args.push(if !v.ty.is_reference_type() {
                         self.builder
-                            .build_alloca(v.ty.llvm_type(self.ns, &self.context), &v.name)
+                            .build_alloca(self.llvm_type(&v.ty), &v.name)
                             .into()
                     } else {
                         self.builder
                             .build_alloca(
-                                v.ty.llvm_type(self.ns, &self.context)
-                                    .ptr_type(AddressSpace::Generic),
+                                self.llvm_type(&v.ty).ptr_type(AddressSpace::Generic),
                                 &v.name,
                             )
                             .into()
@@ -2271,94 +2263,103 @@ impl<'a> Contract<'a> {
             None,
         )
     }
-}
 
-impl ast::PrimitiveType {
-    /// Return the llvm type for this primitive. Non-primitives will panic and should be generated via resolver::Type.llvm_Type()
-    fn llvm_type<'a>(&self, context: &'a Context) -> IntType<'a> {
-        match self {
-            ast::PrimitiveType::Bool => context.bool_type(),
-            ast::PrimitiveType::Int(n) | ast::PrimitiveType::Uint(n) => {
-                context.custom_width_int_type(*n as u32)
-            }
-            ast::PrimitiveType::Address => context.custom_width_int_type(20 * 8),
-            ast::PrimitiveType::Bytes(n) => context.custom_width_int_type(*n as u32 * 8),
-            _ => {
-                panic!("llvm type for {:?} not implemented", self);
-            }
-        }
-    }
-
-    fn stack_based(self) -> bool {
-        match self {
-            ast::PrimitiveType::Bool => false,
-            ast::PrimitiveType::Int(n) => n > 64,
-            ast::PrimitiveType::Uint(n) => n > 64,
-            ast::PrimitiveType::Address => true,
-            ast::PrimitiveType::Bytes(n) => n > 8,
-            _ => unimplemented!(),
-        }
-    }
-}
-
-impl resolver::Type {
     /// Return the llvm type for a variable holding the type, not the type itself
-    fn llvm_var<'a>(&self, ns: &resolver::Contract, context: &'a Context) -> BasicTypeEnum<'a> {
-        let ty = self.llvm_type(ns, context);
-        match self {
-            resolver::Type::Struct(_) | resolver::Type::FixedArray(_, _) => {
-                ty.ptr_type(AddressSpace::Generic).as_basic_type_enum()
+    fn llvm_var(&self, ty: &resolver::Type) -> BasicTypeEnum<'a> {
+        let llvm_ty = self.llvm_type(ty);
+        match ty {
+            resolver::Type::Struct(_) | resolver::Type::Array(_, _) => {
+                llvm_ty.ptr_type(AddressSpace::Generic).as_basic_type_enum()
             }
-            _ => ty,
+            _ => llvm_ty,
         }
     }
 
     /// Return the llvm type for the resolved type.
-    fn llvm_type<'a>(&self, ns: &resolver::Contract, context: &'a Context) -> BasicTypeEnum<'a> {
-        match self {
-            resolver::Type::Primitive(e) => BasicTypeEnum::IntType(e.llvm_type(context)),
-            resolver::Type::Enum(n) => BasicTypeEnum::IntType(ns.enums[*n].ty.llvm_type(context)),
-            resolver::Type::FixedArray(base_ty, dims) => {
-                let ty = base_ty.llvm_var(ns, context);
+    fn llvm_type(&self, ty: &resolver::Type) -> BasicTypeEnum<'a> {
+        match ty {
+            resolver::Type::Primitive(e) => BasicTypeEnum::IntType(e.llvm_type(self.context)),
+            resolver::Type::Enum(n) => {
+                BasicTypeEnum::IntType(self.ns.enums[*n].ty.llvm_type(self.context))
+            }
+            resolver::Type::Array(base_ty, dims) => {
+                let ty = self.llvm_var(base_ty);
 
                 let mut dims = dims.iter();
 
-                let mut aty = ty.array_type(dims.next().unwrap().to_u32().unwrap());
+                let mut aty = match dims.next().unwrap() {
+                    Some(d) => ty.array_type(d.to_u32().unwrap()),
+                    None => return self.module.get_type("vector").unwrap(),
+                };
 
                 for dim in dims {
-                    aty = aty.array_type(dim.to_u32().unwrap());
+                    match dim {
+                        Some(d) => aty = aty.array_type(d.to_u32().unwrap()),
+                        None => return self.module.get_type("vector").unwrap(),
+                    }
                 }
 
                 BasicTypeEnum::ArrayType(aty)
             }
-            resolver::Type::Struct(n) => context
+            resolver::Type::Struct(n) => self
+                .context
                 .struct_type(
-                    &ns.structs[*n]
+                    &self.ns.structs[*n]
                         .fields
                         .iter()
-                        .map(|f| f.ty.llvm_var(ns, context))
+                        .map(|f| self.llvm_var(&f.ty))
                         .collect::<Vec<BasicTypeEnum>>(),
                     false,
                 )
                 .as_basic_type_enum(),
             resolver::Type::Undef => unreachable!(),
-            resolver::Type::Ref(r) => r
-                .llvm_type(ns, context)
+            resolver::Type::Ref(r) => self
+                .llvm_type(r)
                 .ptr_type(AddressSpace::Generic)
                 .as_basic_type_enum(),
             resolver::Type::StorageRef(_) => {
-                BasicTypeEnum::IntType(context.custom_width_int_type(256))
+                BasicTypeEnum::IntType(self.context.custom_width_int_type(256))
             }
         }
     }
+}
 
+impl ast::PrimitiveType {
+    /// Return the llvm type for this primitive. Non-primitives will panic and should be generated via resolver::Type.llvm_Type()
+    fn llvm_type<'a>(&self, context: &'a Context) -> IntType<'a> {
+        match self {
+            ast::PrimitiveType::Bool => context.bool_type(),
+            ast::PrimitiveType::Int(n) | ast::PrimitiveType::Uint(n) => {
+                context.custom_width_int_type(*n as u32)
+            }
+            ast::PrimitiveType::Address => context.custom_width_int_type(20 * 8),
+            ast::PrimitiveType::Bytes(n) => context.custom_width_int_type(*n as u32 * 8),
+            _ => {
+                panic!("llvm type for {:?} not implemented", self);
+            }
+        }
+    }
+
+    fn stack_based(self) -> bool {
+        match self {
+            ast::PrimitiveType::Bool => false,
+            ast::PrimitiveType::Int(n) => n > 64,
+            ast::PrimitiveType::Uint(n) => n > 64,
+            ast::PrimitiveType::Address => true,
+            ast::PrimitiveType::Bytes(n) => n > 8,
+            _ => unimplemented!(),
+        }
+    }
+}
+
+impl resolver::Type {
     /// Is this type an reference type in the solidity language? (struct, array, mapping)
     pub fn is_reference_type(&self) -> bool {
         match self {
             resolver::Type::Primitive(_) => false,
             resolver::Type::Enum(_) => false,
             resolver::Type::Struct(_) => true,
-            resolver::Type::FixedArray(_, _) => true,
+            resolver::Type::Array(_, _) => true,
             resolver::Type::Ref(r) => r.is_reference_type(),
             resolver::Type::StorageRef(r) => r.is_reference_type(),
             resolver::Type::Undef => unreachable!(),
@@ -2371,7 +2372,7 @@ impl resolver::Type {
             resolver::Type::Primitive(e) => e.stack_based(),
             resolver::Type::Enum(_) => false,
             resolver::Type::Struct(_) => true,
-            resolver::Type::FixedArray(_, _) => true,
+            resolver::Type::Array(_, _) => true,
             resolver::Type::Undef => unreachable!(),
             resolver::Type::Ref(_) => false,
             resolver::Type::StorageRef(r) => r.stack_based(),

+ 74 - 67
src/emit/substrate.rs

@@ -405,11 +405,8 @@ impl SubstrateTarget {
                 data,
             ),
             resolver::Type::Struct(n) => {
-                let to = to.unwrap_or_else(|| {
-                    contract
-                        .builder
-                        .build_alloca(ty.llvm_type(contract.ns, contract.context), "")
-                });
+                let to =
+                    to.unwrap_or_else(|| contract.builder.build_alloca(contract.llvm_type(ty), ""));
 
                 for (i, field) in contract.ns.structs[*n].fields.iter().enumerate() {
                     let elem = unsafe {
@@ -426,7 +423,7 @@ impl SubstrateTarget {
                     if field.ty.is_reference_type() {
                         let val = contract
                             .builder
-                            .build_alloca(field.ty.llvm_type(contract.ns, contract.context), "");
+                            .build_alloca(contract.llvm_type(&field.ty), "");
 
                         self.decode_ty(contract, function, &field.ty, Some(val), data);
 
@@ -438,41 +435,41 @@ impl SubstrateTarget {
 
                 to.into()
             }
-            resolver::Type::FixedArray(_, dim) => {
-                let to = to.unwrap_or_else(|| {
-                    contract
-                        .builder
-                        .build_alloca(ty.llvm_type(contract.ns, contract.context), "")
-                });
-
-                contract.emit_static_loop_with_pointer(
-                    function,
-                    0,
-                    dim[0].to_u64().unwrap(),
-                    data,
-                    |index: IntValue<'b>, data: &mut PointerValue<'b>| {
-                        let elem = unsafe {
-                            contract.builder.build_gep(
-                                to,
-                                &[contract.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
-
-                        let ty = ty.array_deref();
-
-                        if ty.is_reference_type() {
-                            let val = contract.builder.build_alloca(
-                                ty.deref().llvm_type(contract.ns, contract.context),
-                                "",
-                            );
-                            self.decode_ty(contract, function, &ty, Some(val), data);
-                            contract.builder.build_store(elem, val);
-                        } else {
-                            self.decode_ty(contract, function, &ty, Some(elem), data);
-                        }
-                    },
-                );
+            resolver::Type::Array(_, dim) => {
+                let to =
+                    to.unwrap_or_else(|| contract.builder.build_alloca(contract.llvm_type(ty), ""));
+
+                if let Some(d) = &dim[0] {
+                    contract.emit_static_loop_with_pointer(
+                        function,
+                        0,
+                        d.to_u64().unwrap(),
+                        data,
+                        |index: IntValue<'b>, data: &mut PointerValue<'b>| {
+                            let elem = unsafe {
+                                contract.builder.build_gep(
+                                    to,
+                                    &[contract.context.i32_type().const_zero(), index],
+                                    "index_access",
+                                )
+                            };
+
+                            let ty = ty.array_deref();
+
+                            if ty.is_reference_type() {
+                                let val = contract
+                                    .builder
+                                    .build_alloca(contract.llvm_type(&ty.deref()), "");
+                                self.decode_ty(contract, function, &ty, Some(val), data);
+                                contract.builder.build_store(elem, val);
+                            } else {
+                                self.decode_ty(contract, function, &ty, Some(elem), data);
+                            }
+                        },
+                    );
+                } else {
+                    // FIXME
+                }
 
                 to.into()
             }
@@ -619,30 +616,34 @@ impl SubstrateTarget {
             resolver::Type::Enum(n) => {
                 self.encode_primitive(contract, contract.ns.enums[*n].ty, *data, arg);
             }
-            resolver::Type::FixedArray(_, dim) => {
-                contract.emit_static_loop_with_pointer(
-                    function,
-                    0,
-                    dim[0].to_u64().unwrap(),
-                    data,
-                    |index, data| {
-                        let mut elem = unsafe {
-                            contract.builder.build_gep(
-                                arg.into_pointer_value(),
-                                &[contract.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
-
-                        let ty = ty.array_deref();
-
-                        if ty.is_reference_type() {
-                            elem = contract.builder.build_load(elem, "").into_pointer_value()
-                        }
-
-                        self.encode_ty(contract, function, &ty, elem.into(), data);
-                    },
-                );
+            resolver::Type::Array(_, dim) => {
+                if let Some(d) = &dim[0] {
+                    contract.emit_static_loop_with_pointer(
+                        function,
+                        0,
+                        d.to_u64().unwrap(),
+                        data,
+                        |index, data| {
+                            let mut elem = unsafe {
+                                contract.builder.build_gep(
+                                    arg.into_pointer_value(),
+                                    &[contract.context.i32_type().const_zero(), index],
+                                    "index_access",
+                                )
+                            };
+
+                            let ty = ty.array_deref();
+
+                            if ty.is_reference_type() {
+                                elem = contract.builder.build_load(elem, "").into_pointer_value()
+                            }
+
+                            self.encode_ty(contract, function, &ty, elem.into(), data);
+                        },
+                    );
+                } else {
+                    // FIXME
+                }
             }
             resolver::Type::Struct(n) => {
                 for (i, field) in contract.ns.structs[*n].fields.iter().enumerate() {
@@ -689,9 +690,15 @@ impl SubstrateTarget {
                 .iter()
                 .map(|f| self.encoded_length(&f.ty, contract))
                 .sum(),
-            resolver::Type::FixedArray(ty, dims) => {
+            resolver::Type::Array(ty, dims) => {
                 self.encoded_length(ty, contract)
-                    * dims.iter().map(|d| d.to_u64().unwrap()).product::<u64>()
+                    * dims
+                        .iter()
+                        .map(|d| match d {
+                            Some(d) => d.to_u64().unwrap(),
+                            None => 1,
+                        })
+                        .product::<u64>()
             }
             resolver::Type::Undef => unreachable!(),
             resolver::Type::StorageRef(_) => unreachable!(),

+ 1 - 1
src/resolver/cfg.rs

@@ -1350,7 +1350,7 @@ impl resolver::Type {
             resolver::Type::Primitive(e) => e.default(),
             resolver::Type::Enum(e) => ns.enums[*e].ty.default(),
             resolver::Type::Undef => unreachable!(),
-            resolver::Type::FixedArray(_, _) => unreachable!(),
+            resolver::Type::Array(_, _) => unreachable!(),
             resolver::Type::Struct(_) => {
                 Expression::StructLiteral(ast::Loc(0, 0), self.clone(), Vec::new())
             }

+ 20 - 10
src/resolver/expression.rs

@@ -284,7 +284,7 @@ fn get_int_length(
             ));
             Err(())
         }
-        resolver::Type::FixedArray(_, _) => {
+        resolver::Type::Array(_, _) => {
             errors.push(Output::error(
                 *l_loc,
                 format!("type array {} not allowed", l.to_string(ns)),
@@ -2321,9 +2321,12 @@ pub fn expression(
                         ));
                     }
                 }
-                resolver::Type::FixedArray(_, dim) => {
+                resolver::Type::Array(_, dim) => {
                     if id.name == "length" {
-                        return bigint_to_expression(loc, dim.last().unwrap(), errors);
+                        return match dim.last().unwrap() {
+                            None => unimplemented!(),
+                            Some(d) => bigint_to_expression(loc, d, errors),
+                        };
                     }
                 }
                 resolver::Type::StorageRef(r) => match *r {
@@ -2362,9 +2365,12 @@ pub fn expression(
                             ));
                         }
                     }
-                    resolver::Type::FixedArray(_, dim) => {
+                    resolver::Type::Array(_, dim) => {
                         if id.name == "length" {
-                            return bigint_to_expression(loc, dim.last().unwrap(), errors);
+                            return match dim.last().unwrap() {
+                                None => unimplemented!(),
+                                Some(d) => bigint_to_expression(loc, d, errors),
+                            };
                         }
                     }
                     _ => {}
@@ -2564,7 +2570,11 @@ fn array_subscript(
 
     let array_length = match array_ty.deref() {
         resolver::Type::Primitive(ast::PrimitiveType::Bytes(n)) => BigInt::from(*n),
-        resolver::Type::FixedArray(_, _) => array_ty.array_length().clone(),
+        resolver::Type::Array(_, _) => match array_ty.array_length() {
+            None => unimplemented!(),
+            Some(l) => l,
+        }
+        .clone(),
         _ => {
             errors.push(Output::error(
                 array.loc(),
@@ -2780,7 +2790,7 @@ fn array_subscript(
                     res_ty,
                 ))
             }
-            resolver::Type::FixedArray(_, _) => Ok((
+            resolver::Type::Array(_, _) => Ok((
                 Expression::ArraySubscript(
                     *loc,
                     Box::new(cast(
@@ -3167,11 +3177,11 @@ fn resolve_array_literal(
         exprs.push(other);
     }
 
-    let aty = resolver::Type::FixedArray(
+    let aty = resolver::Type::Array(
         Box::new(ty),
         dims.iter()
-            .map(|n| BigInt::from_u32(*n).unwrap())
-            .collect::<Vec<BigInt>>(),
+            .map(|n| Some(BigInt::from_u32(*n).unwrap()))
+            .collect::<Vec<Option<BigInt>>>(),
     );
 
     if vartab.is_none() {

+ 68 - 35
src/resolver/mod.rs

@@ -29,7 +29,7 @@ pub type ArrayDimension = Option<(ast::Loc, BigInt)>;
 #[derive(PartialEq, Clone, Debug)]
 pub enum Type {
     Primitive(ast::PrimitiveType),
-    FixedArray(Box<Type>, Vec<BigInt>),
+    Array(Box<Type>, Vec<Option<BigInt>>),
     Enum(usize),
     Struct(usize),
     Ref(Box<Type>),
@@ -43,10 +43,15 @@ impl Type {
             Type::Primitive(e) => e.to_string(),
             Type::Enum(n) => format!("enum {}.{}", ns.name, ns.enums[*n].name),
             Type::Struct(n) => format!("struct {}.{}", ns.name, ns.structs[*n].name),
-            Type::FixedArray(ty, len) => format!(
+            Type::Array(ty, len) => format!(
                 "{}{}",
                 ty.to_string(ns),
-                len.iter().map(|l| format!("[{}]", l)).collect::<String>()
+                len.iter()
+                    .map(|l| match l {
+                        None => "[]".to_string(),
+                        Some(l) => format!("[{}]", l),
+                    })
+                    .collect::<String>()
             ),
             Type::Ref(r) => r.to_string(ns),
             Type::StorageRef(ty) => format!("storage {}", ty.to_string(ns)),
@@ -58,10 +63,15 @@ impl Type {
         match self {
             Type::Primitive(e) => e.to_string(),
             Type::Enum(n) => ns.enums[*n].ty.to_string(),
-            Type::FixedArray(ty, len) => format!(
+            Type::Array(ty, len) => format!(
                 "{}{}",
                 ty.to_signature_string(ns),
-                len.iter().map(|l| format!("[{}]", l)).collect::<String>()
+                len.iter()
+                    .map(|l| match l {
+                        None => "[]".to_string(),
+                        Some(l) => format!("[{}]", l),
+                    })
+                    .collect::<String>()
             ),
             Type::Ref(r) => r.to_string(ns),
             Type::StorageRef(r) => r.to_string(ns),
@@ -75,10 +85,10 @@ impl Type {
     pub fn array_deref(&self) -> Self {
         match self {
             Type::Ref(t) => t.array_deref(),
-            Type::FixedArray(ty, dim) if dim.len() > 1 => {
-                Type::FixedArray(ty.clone(), dim[..dim.len() - 1].to_vec())
+            Type::Array(ty, dim) if dim.len() > 1 => {
+                Type::Array(ty.clone(), dim[..dim.len() - 1].to_vec())
             }
-            Type::FixedArray(ty, dim) if dim.len() == 1 => Type::Ref(Box::new(*ty.clone())),
+            Type::Array(ty, dim) if dim.len() == 1 => Type::Ref(Box::new(*ty.clone())),
             _ => panic!("deref on non-array"),
         }
     }
@@ -87,21 +97,22 @@ impl Type {
     /// array types and will cause a panic otherwise.
     pub fn storage_deref(&self) -> Self {
         match self {
-            Type::FixedArray(ty, dim) if dim.len() > 1 => Type::StorageRef(Box::new(
-                Type::FixedArray(ty.clone(), dim[..dim.len() - 1].to_vec()),
-            )),
-            Type::FixedArray(ty, dim) if dim.len() == 1 => Type::StorageRef(Box::new(*ty.clone())),
+            Type::Array(ty, dim) if dim.len() > 1 => Type::StorageRef(Box::new(Type::Array(
+                ty.clone(),
+                dim[..dim.len() - 1].to_vec(),
+            ))),
+            Type::Array(ty, dim) if dim.len() == 1 => Type::StorageRef(Box::new(*ty.clone())),
             _ => panic!("deref on non-array"),
         }
     }
 
     /// Give the length of the outer array. This can only be called on array types
     /// and will panic otherwise.
-    pub fn array_length(&self) -> &BigInt {
+    pub fn array_length(&self) -> Option<&BigInt> {
         match self {
             Type::StorageRef(ty) => ty.array_length(),
             Type::Ref(ty) => ty.array_length(),
-            Type::FixedArray(_, dim) => dim.last().unwrap(),
+            Type::Array(_, dim) => dim.last().unwrap().as_ref(),
             _ => panic!("array_length on non-array"),
         }
     }
@@ -117,7 +128,17 @@ impl Type {
             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) => ty.size_hint(ns).mul(dims.iter().product::<BigInt>()),
+            Type::Array(ty, dims) => {
+                let pointer_size = BigInt::from(4);
+                ty.size_hint(ns).mul(
+                    dims.iter()
+                        .map(|d| match d {
+                            None => &pointer_size,
+                            Some(d) => d,
+                        })
+                        .product::<BigInt>(),
+                )
+            }
             Type::Struct(n) => ns.structs[*n]
                 .fields
                 .iter()
@@ -149,7 +170,7 @@ impl Type {
             Type::Primitive(e) => e.ordered(),
             Type::Enum(_) => false,
             Type::Struct(_) => unreachable!(),
-            Type::FixedArray(_, _) => unreachable!(),
+            Type::Array(_, _) => unreachable!(),
             Type::Undef => unreachable!(),
             Type::Ref(r) => r.ordered(),
             Type::StorageRef(r) => r.ordered(),
@@ -172,7 +193,18 @@ impl Type {
                 .map(|f| f.ty.storage_slots(ns))
                 .sum(),
             Type::Undef => unreachable!(),
-            Type::FixedArray(ty, dims) => ty.storage_slots(ns) * dims.iter().product::<BigInt>(),
+            Type::Array(ty, dims) => {
+                let one = BigInt::one();
+
+                ty.storage_slots(ns)
+                    * dims
+                        .iter()
+                        .map(|l| match l {
+                            None => &one,
+                            Some(l) => l,
+                        })
+                        .product::<BigInt>()
+            }
         }
     }
 
@@ -181,7 +213,7 @@ impl Type {
     /// allowed be storage are welcome.
     pub fn can_have_data_location(&self) -> bool {
         match self {
-            Type::FixedArray(_, _) => true,
+            Type::Array(_, _) => true,
             Type::Struct(_) => true,
             _ => false,
         }
@@ -307,10 +339,15 @@ impl FunctionDecl {
                         Type::Primitive(e) => e.to_string(),
                         Type::Enum(i) => ns.enums[*i].name.to_owned(),
                         Type::Struct(i) => ns.structs[*i].name.to_owned(),
-                        Type::FixedArray(ty, len) => format!(
+                        Type::Array(ty, len) => format!(
                             "{}{}",
                             ty.to_string(ns),
-                            len.iter().map(|r| format!(":{}", r)).collect::<String>()
+                            len.iter()
+                                .map(|r| match r {
+                                    None => ":".to_string(),
+                                    Some(r) => format!(":{}", r),
+                                })
+                                .collect::<String>()
                         ),
                         Type::Undef => unreachable!(),
                         Type::Ref(r) => type_to_wasm_name(r, ns),
@@ -439,13 +476,12 @@ impl Contract {
     /// 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: &mut Vec<Output>) -> Result<Type, ()> {
         fn resolve_dimensions(
-            dimensions: &[Option<(ast::Loc, BigInt)>],
+            ast_dimensions: &[Option<(ast::Loc, BigInt)>],
             errors: &mut Vec<Output>,
-        ) -> Result<Vec<BigInt>, ()> {
-            let mut fixed = true;
-            let mut fixed_dimensions = Vec::new();
+        ) -> Result<Vec<Option<BigInt>>, ()> {
+            let mut dimensions = Vec::new();
 
-            for d in dimensions.iter() {
+            for d in ast_dimensions.iter() {
                 if let Some((loc, n)) = d {
                     if n.is_zero() {
                         errors.push(Output::decl_error(
@@ -460,16 +496,13 @@ impl Contract {
                         ));
                         return Err(());
                     }
-                    fixed_dimensions.push(n.clone());
+                    dimensions.push(Some(n.clone()));
                 } else {
-                    fixed = false;
+                    dimensions.push(None);
                 }
             }
-            if fixed {
-                Ok(fixed_dimensions)
-            } else {
-                unimplemented!();
-            }
+
+            Ok(dimensions)
         }
 
         match id {
@@ -484,7 +517,7 @@ impl Contract {
                     });
                 }
 
-                Ok(Type::FixedArray(
+                Ok(Type::Array(
                     Box::new(Type::Primitive(*p)),
                     resolve_dimensions(&dimensions, errors)?,
                 ))
@@ -501,12 +534,12 @@ impl Contract {
                         Err(())
                     }
                     Some(Symbol::Enum(_, n)) if dimensions.is_empty() => Ok(Type::Enum(*n)),
-                    Some(Symbol::Enum(_, n)) => Ok(Type::FixedArray(
+                    Some(Symbol::Enum(_, n)) => Ok(Type::Array(
                         Box::new(Type::Enum(*n)),
                         resolve_dimensions(&dimensions, errors)?,
                     )),
                     Some(Symbol::Struct(_, n)) if dimensions.is_empty() => Ok(Type::Struct(*n)),
-                    Some(Symbol::Struct(_, n)) => Ok(Type::FixedArray(
+                    Some(Symbol::Struct(_, n)) => Ok(Type::Array(
                         Box::new(Type::Struct(*n)),
                         resolve_dimensions(&dimensions, errors)?,
                     )),

BIN
stdlib/stdlib.bc


+ 20 - 13
stdlib/stdlib.c

@@ -3,18 +3,6 @@
 #include <stddef.h>
 #include <stdbool.h>
 
-/*
- * The external interface
- */
-
-/*
- * Retrieve contract storage for this account. If nothing is stored at the key,
- * set the memory at dest to 0. If the storage is shorter, pad the remaining bytes
- * with 0.
- */
-extern void get_storage32(uint32_t key, void *dest, int32_t length);
-extern void set_storage32(uint32_t key, void *src, int32_t length);
-
 /*
  */
 __attribute__((visibility("hidden"))) void __memset8(void *_dest, uint64_t val, size_t length)
@@ -393,4 +381,23 @@ char *__u256ptohex(uint8_t *v, char *str)
 	}
 
 	return str;
-}
+}
+
+/*
+ * Vector is used for dynamic array
+ */
+struct vector
+{
+	uint32_t size;
+	uint32_t len;
+	uint8_t data[];
+};
+
+__attribute__((visibility("hidden"))) struct vector *vector_new(uint32_t members, uint32_t size)
+{
+	struct vector *v = __malloc(sizeof(*v) + members * size);
+	v->size = members;
+	v->len = members;
+
+	return v;
+}