Skip to content

Commit cf23885

Browse files
authored
Merge pull request #30 from flintlang/move-arrays
Array functionality in Move
2 parents e13550a + 479307a commit cf23885

File tree

22 files changed

+317
-105
lines changed

22 files changed

+317
-105
lines changed

docs/state_of_flint-2.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ The result of this is that at the moment, it is not clear if it is possible to d
9292
To allow calls into our Move contracts, we provide a wrapper method for each public function which takes in an address and borrows the resource published at that address, which is then passed into the inner function. In order to facilitate the minimum amount of runtime checking of type states and caller protections (which are only required for external calls), we also perform these checks inside the wrapper methods.
9393

9494
#### Arrays and Dictionaries
95-
- As explained in the [github issue](https://github.com/flintlang/flint-2/issues/20), accessing arrays is currently unsupported in move. This was due to a libra update which introduced more sophisticated build in data structures, which left the previous implementation broken. We do not invisage that this will be a particularly difficult fix, as arrays are already stored and constructed correctly, and so it seems that it is only move subscript expressions that will need to be altered. At the moment, due to the previous implementation, a subscript results in a runtime function being called on the array. This should be removed and replaced with the correct native method calls, as stated in the libra link in the aforementioned issue.
96-
95+
- Array values are accessed using the libra vector functions ```Vector.borrow()``` and ```Vector.borrow_mut()```, depending on whether the value should be mutated or not. There are also three runtime functions, ```Flint_array_insert()```, ```Flint_array_remove``` and ```Flint_array_length```, to allow elements to be inserted and removed from dynamic arrays, and to get the length of the array.
9796
- In Move, each value in the dictionary is wrapped in a resource, and is stored at the address given by the key. This means that dictionaries are restricted to only having keys of type ```address```, but since dictionaries in Flint can have any key type, this should be implemented.
9897

9998
### eWASM Translation

src/environment/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ mod expr_type_check;
1010

1111
pub(crate) const FLINT_GLOBAL: &str = "Flint_Global";
1212
pub(crate) const FLINT_GLOBAL_TRANSFER: &str = "Flint_transfer";
13+
pub(crate) const FLINT_GLOBAL_ARRAY_REMOVE: &str = "Flint_array_remove";
14+
pub(crate) const FLINT_GLOBAL_ARRAY_INSERT: &str = "Flint_array_insert";
15+
pub(crate) const FLINT_GLOBAL_ARRAY_LENGTH: &str = "Flint_array_length";
1316
const FLINT_RUNTIME_PREFIX: &str = "Flint_";
1417

1518
#[derive(Debug, Default, Clone)]

src/ewasm/codegen/runtime_functions.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
2323

2424
let func_type = address_type.fn_type(&[], false);
2525

26-
let func_val = self.module.add_function(LLVMPreProcessor::CALLER_WRAPPER_NAME, func_type, None);
26+
let func_val =
27+
self.module
28+
.add_function(LLVMPreProcessor::CALLER_WRAPPER_NAME, func_type, None);
2729

2830
let bb = self.context.append_basic_block(func_val, "entry");
2931

@@ -76,7 +78,9 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
7678
.context
7779
.i64_type()
7880
.fn_type(&[param_type, param_type], false);
79-
let exp = self.module.add_function(Codegen::EXPONENTIATION_NAME, int_type, None);
81+
let exp = self
82+
.module
83+
.add_function(Codegen::EXPONENTIATION_NAME, int_type, None);
8084
let bb = self.context.append_basic_block(exp, "entry");
8185
self.builder.position_at_end(bb);
8286

src/ewasm/expressions/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,10 @@ impl<'a> LLVMBinaryExpression<'a> {
437437
}
438438
BinOp::Power => {
439439
if lhs.is_int_value() && rhs.is_int_value() {
440-
let exp_func = codegen.module.get_function(Codegen::EXPONENTIATION_NAME).unwrap();
440+
let exp_func = codegen
441+
.module
442+
.get_function(Codegen::EXPONENTIATION_NAME)
443+
.unwrap();
441444

442445
codegen
443446
.builder

src/ewasm/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ pub fn generate(module: &Module, context: &mut Context) {
180180
let wasm = fs::read(Path::new(
181181
get_path(TEMPORARY_DIRECTORY_NAME, "wasm").as_str(),
182182
))
183-
.expect("Could not read wasm");
183+
.expect("Could not read wasm");
184184
let mut as_wat = wasm2wat(wasm).expect("Could not convert wasm to wat");
185185

186186
// Shift final module closing curly brace onto its own line so it is not removed when trimming exports
@@ -198,7 +198,7 @@ pub fn generate(module: &Module, context: &mut Context) {
198198
Path::new(get_path(TEMPORARY_DIRECTORY_NAME, "wat").as_str()),
199199
&as_wat.as_bytes(),
200200
)
201-
.expect("Could not create temporary wat file");
201+
.expect("Could not create temporary wat file");
202202

203203
let post_processed_wasm =
204204
wat2wasm(as_wat.as_bytes()).expect("Could not convert wat to wasm");
@@ -213,14 +213,14 @@ pub fn generate(module: &Module, context: &mut Context) {
213213
Path::new(get_path(TEMPORARY_DIRECTORY_NAME, "wat").as_str()),
214214
Path::new(get_path(OUTPUT_DIRECTORY_NAME, "wat").as_str()),
215215
)
216-
.expect("Could not copy wat file from temporary dir to output dir");
216+
.expect("Could not copy wat file from temporary dir to output dir");
217217

218218
// TODO remove this for the final release, but for testing the LLVM, we need to be able to read the LLVM files
219219
fs::copy(
220220
Path::new(get_path(TEMPORARY_DIRECTORY_NAME, "ll").as_str()),
221221
Path::new(get_path(OUTPUT_DIRECTORY_NAME, "ll").as_str()),
222222
)
223-
.expect("Could not copy ll file from temporary dir to output dir");
223+
.expect("Could not copy ll file from temporary dir to output dir");
224224

225225
// Delete all tmp files
226226
fs::remove_dir_all(tmp_path).expect("Could not remove temporary directory");

src/ewasm/structs/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn add_initialiser_function_declaration(
7373
LLVMType {
7474
ast_type: &param.type_assignment,
7575
}
76-
.generate(codegen)
76+
.generate(codegen)
7777
})
7878
.collect::<Vec<BasicTypeEnum>>();
7979

src/ewasm/utils.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ pub fn generate_caller_variable<'ctx>(
2222
let caller_address = codegen
2323
.builder
2424
.build_call(
25-
codegen.module.get_function(LLVMPreProcessor::CALLER_WRAPPER_NAME).unwrap(),
25+
codegen
26+
.module
27+
.get_function(LLVMPreProcessor::CALLER_WRAPPER_NAME)
28+
.unwrap(),
2629
&[],
2730
"tmp_call",
2831
)

src/moveir/contract.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ impl MoveContract {
190190
identifier: p.identifier,
191191
position: MovePosition::Left,
192192
}
193-
.generate(&function_context, false, false)
193+
.generate(&function_context, false, false)
194194
.to_string()
195195
})
196196
.collect();
@@ -203,7 +203,7 @@ impl MoveContract {
203203
identifier: p.identifier,
204204
position: MovePosition::Left,
205205
}
206-
.generate(&function_context, true, false)
206+
.generate(&function_context, true, false)
207207
.to_string()
208208
})
209209
.collect::<Vec<String>>()
@@ -300,7 +300,7 @@ impl MoveContract {
300300
expression: e,
301301
position: Default::default(),
302302
}
303-
.generate(&function_context)
303+
.generate(&function_context)
304304
})
305305
.collect();
306306

@@ -345,7 +345,7 @@ impl MoveContract {
345345
expression: (**expr).clone(),
346346
position: Default::default(),
347347
}
348-
.generate(&function_context),
348+
.generate(&function_context),
349349
),
350350
},
351351
));

src/moveir/declaration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl MoveVariableDeclaration {
4141
self.declaration.variable_type.clone(),
4242
Option::from(function_context.environment.clone()),
4343
)
44-
.generate(function_context);
44+
.generate(function_context);
4545

4646
if self.declaration.identifier.is_self() {
4747
return MoveIRExpression::VariableDeclaration(MoveIRVariableDeclaration {

src/moveir/expression.rs

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use super::declaration::MoveVariableDeclaration;
44
use super::function::FunctionContext;
55
use super::identifier::MoveIdentifier;
66
use super::ir::{
7-
MoveIRExpression, MoveIRFunctionCall, MoveIRLiteral, MoveIROperation, MoveIRVector,
7+
MoveIRAssignment, MoveIRExpression, MoveIRFunctionCall, MoveIRLiteral, MoveIROperation,
8+
MoveIRVector,
89
};
910
use super::literal::MoveLiteralToken;
1011
use super::property_access::MovePropertyAccess;
@@ -30,12 +31,12 @@ impl MoveExpression {
3031
identifier: i,
3132
position: self.position.clone(),
3233
}
33-
.generate(function_context, false, false),
34+
.generate(function_context, false, false),
3435
Expression::BinaryExpression(b) => MoveBinaryExpression {
3536
expression: b,
3637
position: self.position.clone(),
3738
}
38-
.generate(function_context),
39+
.generate(function_context),
3940
Expression::InoutExpression(i) => MoveInoutExpression {
4041
expression: i,
4142
position: self.position.clone(),
@@ -58,7 +59,7 @@ impl MoveExpression {
5859
expression: *b.expression,
5960
position: Default::default(),
6061
}
61-
.generate(function_context),
62+
.generate(function_context),
6263
Expression::AttemptExpression(_) => panic!("Should have been removed in preprocessor"),
6364
Expression::Literal(l) => {
6465
MoveIRExpression::Literal(MoveLiteralToken { token: l }.generate())
@@ -140,7 +141,7 @@ impl MoveCastExpression {
140141
expression: (*self.expression.expression).clone(),
141142
position: Default::default(),
142143
}
143-
.generate(function_context);
144+
.generate(function_context);
144145

145146
if original_type_information.0 <= target_type_information.0 {
146147
return expression_code;
@@ -193,15 +194,16 @@ impl MoveSubscriptExpression {
193194
expression: *index.clone(),
194195
position: Default::default(),
195196
}
196-
.generate(function_context);
197+
.generate(function_context);
197198

198199
let identifier = self.expression.base_expression.clone();
199200

200201
let identifier_code = MoveIdentifier {
201202
identifier,
202-
position: self.position.clone(),
203+
position: MovePosition::Left,
203204
}
204-
.generate(function_context, false, false);
205+
.generate(function_context, false, true);
206+
205207
let base_type = function_context.environment.get_expression_type(
206208
&Expression::Identifier(self.expression.base_expression.clone()),
207209
&function_context.enclosing_type.clone(),
@@ -212,8 +214,35 @@ impl MoveSubscriptExpression {
212214

213215
if let MovePosition::Left = self.position.clone() {
214216
return match base_type {
215-
Type::FixedSizedArrayType(_) | Type::ArrayType(_) => {
216-
MoveRuntimeFunction::append_to_array_int(identifier_code, rhs)
217+
Type::FixedSizedArrayType(a) => {
218+
let elem_type = MoveType::move_type(
219+
*a.key_type,
220+
Some(function_context.environment.clone()),
221+
)
222+
.generate(function_context);
223+
224+
MoveIRExpression::Assignment(MoveIRAssignment {
225+
identifier: format!(
226+
"*Vector.borrow_mut<{}>({}, {})",
227+
elem_type, identifier_code, index
228+
),
229+
expression: Box::from(rhs),
230+
})
231+
}
232+
Type::ArrayType(a) => {
233+
let elem_type = MoveType::move_type(
234+
*a.key_type,
235+
Some(function_context.environment.clone()),
236+
)
237+
.generate(function_context);
238+
239+
MoveIRExpression::Assignment(MoveIRAssignment {
240+
identifier: format!(
241+
"*Vector.borrow_mut<{}>({}, {})",
242+
elem_type, identifier_code, index
243+
),
244+
expression: Box::from(rhs),
245+
})
217246
}
218247
Type::DictionaryType(_) => {
219248
let f_name = format!(
@@ -242,15 +271,31 @@ impl MoveSubscriptExpression {
242271
}
243272

244273
match base_type {
245-
Type::FixedSizedArrayType(_) | Type::ArrayType(_) => {
246-
let identifier = self.expression.base_expression.clone();
247-
248-
let identifier_code = MoveIdentifier {
249-
identifier,
250-
position: self.position.clone(),
251-
}
252-
.generate(function_context, false, true);
253-
MoveRuntimeFunction::get_from_array_int(identifier_code, index)
274+
Type::FixedSizedArrayType(a) => {
275+
let identifier_code = MoveIRExpression::Operation(MoveIROperation::Reference(
276+
Box::from(identifier_code),
277+
));
278+
let elem_type =
279+
MoveType::move_type(*a.key_type, Some(function_context.environment.clone()))
280+
.generate(function_context);
281+
282+
MoveIRExpression::Inline(format!(
283+
"*Vector.borrow<{}>({}, {})",
284+
elem_type, identifier_code, index
285+
))
286+
}
287+
Type::ArrayType(a) => {
288+
let identifier_code = MoveIRExpression::Operation(MoveIROperation::Reference(
289+
Box::from(identifier_code),
290+
));
291+
let elem_type =
292+
MoveType::move_type(*a.key_type, Some(function_context.environment.clone()))
293+
.generate(function_context);
294+
295+
MoveIRExpression::Inline(format!(
296+
"*Vector.borrow<{}>({}, {})",
297+
elem_type, identifier_code, index
298+
))
254299
}
255300
Type::DictionaryType(_) => {
256301
let f_name = format!(
@@ -287,7 +332,7 @@ impl MoveInoutExpression {
287332
expression: *self.expression.expression.clone(),
288333
position: self.position.clone(),
289334
}
290-
.generate(function_context);
335+
.generate(function_context);
291336
}
292337

293338
if let MovePosition::Accessed = self.position {
@@ -298,7 +343,7 @@ impl MoveInoutExpression {
298343
expression: *self.expression.expression.clone(),
299344
position: MovePosition::Left,
300345
}
301-
.generate(function_context),
346+
.generate(function_context),
302347
)));
303348
}
304349
}
@@ -308,7 +353,7 @@ impl MoveInoutExpression {
308353
expression: *self.expression.expression.clone(),
309354
position: self.position.clone(),
310355
}
311-
.generate(function_context);
356+
.generate(function_context);
312357
}
313358

314359
let expression = self.expression.clone();
@@ -317,7 +362,7 @@ impl MoveInoutExpression {
317362
expression: *expression.expression,
318363
position: MovePosition::Inout,
319364
}
320-
.generate(function_context),
365+
.generate(function_context),
321366
)))
322367
}
323368
}
@@ -335,7 +380,7 @@ impl MoveBinaryExpression {
335380
function_call: f,
336381
module_name: "Self".to_string(),
337382
}
338-
.generate(function_context);
383+
.generate(function_context);
339384
}
340385
return MovePropertyAccess {
341386
left: *self.expression.lhs_expression.clone(),
@@ -450,10 +495,10 @@ pub fn is_signer_type(expression: &Expression, function_context: &FunctionContex
450495
if let Some(identifier_type) = function_context.scope_context.type_for(&id.token) {
451496
return identifier_type
452497
== Type::UserDefinedType(Identifier {
453-
token: MovePreProcessor::SIGNER_TYPE.to_string(),
454-
enclosing_type: None,
455-
line_info: Default::default(),
456-
});
498+
token: MovePreProcessor::SIGNER_TYPE.to_string(),
499+
enclosing_type: None,
500+
line_info: Default::default(),
501+
});
457502
}
458503
}
459504
false

0 commit comments

Comments
 (0)