Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array functionality in Move #30

Merged
merged 7 commits into from
Sep 16, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/state_of_flint-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ The result of this is that at the moment, it is not clear if it is possible to d
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.

#### Arrays and Dictionaries
- 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.

- 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.
- 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.

### eWASM Translation
Expand Down
3 changes: 3 additions & 0 deletions src/environment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ mod expr_type_check;

pub(crate) const FLINT_GLOBAL: &str = "Flint_Global";
pub(crate) const FLINT_GLOBAL_TRANSFER: &str = "Flint_transfer";
pub(crate) const FLINT_GLOBAL_ARRAY_REMOVE: &str = "Flint_array_remove";
pub(crate) const FLINT_GLOBAL_ARRAY_INSERT: &str = "Flint_array_insert";
pub(crate) const FLINT_GLOBAL_ARRAY_LENGTH: &str = "Flint_array_length";
const FLINT_RUNTIME_PREFIX: &str = "Flint_";

#[derive(Debug, Default, Clone)]
Expand Down
8 changes: 6 additions & 2 deletions src/ewasm/codegen/runtime_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {

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

let func_val = self.module.add_function(LLVMPreProcessor::CALLER_WRAPPER_NAME, func_type, None);
let func_val =
self.module
.add_function(LLVMPreProcessor::CALLER_WRAPPER_NAME, func_type, None);

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

Expand Down Expand Up @@ -76,7 +78,9 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
.context
.i64_type()
.fn_type(&[param_type, param_type], false);
let exp = self.module.add_function(Codegen::EXPONENTIATION_NAME, int_type, None);
let exp = self
.module
.add_function(Codegen::EXPONENTIATION_NAME, int_type, None);
let bb = self.context.append_basic_block(exp, "entry");
self.builder.position_at_end(bb);

Expand Down
5 changes: 4 additions & 1 deletion src/ewasm/expressions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,10 @@ impl<'a> LLVMBinaryExpression<'a> {
}
BinOp::Power => {
if lhs.is_int_value() && rhs.is_int_value() {
let exp_func = codegen.module.get_function(Codegen::EXPONENTIATION_NAME).unwrap();
let exp_func = codegen
.module
.get_function(Codegen::EXPONENTIATION_NAME)
.unwrap();

codegen
.builder
Expand Down
8 changes: 4 additions & 4 deletions src/ewasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ pub fn generate(module: &Module, context: &mut Context) {
let wasm = fs::read(Path::new(
get_path(TEMPORARY_DIRECTORY_NAME, "wasm").as_str(),
))
.expect("Could not read wasm");
.expect("Could not read wasm");
let mut as_wat = wasm2wat(wasm).expect("Could not convert wasm to wat");

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

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

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

// Delete all tmp files
fs::remove_dir_all(tmp_path).expect("Could not remove temporary directory");
Expand Down
2 changes: 1 addition & 1 deletion src/ewasm/structs/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub fn add_initialiser_function_declaration(
LLVMType {
ast_type: &param.type_assignment,
}
.generate(codegen)
.generate(codegen)
})
.collect::<Vec<BasicTypeEnum>>();

Expand Down
5 changes: 4 additions & 1 deletion src/ewasm/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ pub fn generate_caller_variable<'ctx>(
let caller_address = codegen
.builder
.build_call(
codegen.module.get_function(LLVMPreProcessor::CALLER_WRAPPER_NAME).unwrap(),
codegen
.module
.get_function(LLVMPreProcessor::CALLER_WRAPPER_NAME)
.unwrap(),
&[],
"tmp_call",
)
Expand Down
8 changes: 4 additions & 4 deletions src/moveir/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ impl MoveContract {
identifier: p.identifier,
position: MovePosition::Left,
}
.generate(&function_context, false, false)
.generate(&function_context, false, false)
.to_string()
})
.collect();
Expand All @@ -203,7 +203,7 @@ impl MoveContract {
identifier: p.identifier,
position: MovePosition::Left,
}
.generate(&function_context, true, false)
.generate(&function_context, true, false)
.to_string()
})
.collect::<Vec<String>>()
Expand Down Expand Up @@ -300,7 +300,7 @@ impl MoveContract {
expression: e,
position: Default::default(),
}
.generate(&function_context)
.generate(&function_context)
})
.collect();

Expand Down Expand Up @@ -345,7 +345,7 @@ impl MoveContract {
expression: (**expr).clone(),
position: Default::default(),
}
.generate(&function_context),
.generate(&function_context),
),
},
));
Expand Down
2 changes: 1 addition & 1 deletion src/moveir/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl MoveVariableDeclaration {
self.declaration.variable_type.clone(),
Option::from(function_context.environment.clone()),
)
.generate(function_context);
.generate(function_context);

if self.declaration.identifier.is_self() {
return MoveIRExpression::VariableDeclaration(MoveIRVariableDeclaration {
Expand Down
101 changes: 73 additions & 28 deletions src/moveir/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use super::declaration::MoveVariableDeclaration;
use super::function::FunctionContext;
use super::identifier::MoveIdentifier;
use super::ir::{
MoveIRExpression, MoveIRFunctionCall, MoveIRLiteral, MoveIROperation, MoveIRVector,
MoveIRAssignment, MoveIRExpression, MoveIRFunctionCall, MoveIRLiteral, MoveIROperation,
MoveIRVector,
};
use super::literal::MoveLiteralToken;
use super::property_access::MovePropertyAccess;
Expand All @@ -30,12 +31,12 @@ impl MoveExpression {
identifier: i,
position: self.position.clone(),
}
.generate(function_context, false, false),
.generate(function_context, false, false),
Expression::BinaryExpression(b) => MoveBinaryExpression {
expression: b,
position: self.position.clone(),
}
.generate(function_context),
.generate(function_context),
Expression::InoutExpression(i) => MoveInoutExpression {
expression: i,
position: self.position.clone(),
Expand All @@ -58,7 +59,7 @@ impl MoveExpression {
expression: *b.expression,
position: Default::default(),
}
.generate(function_context),
.generate(function_context),
Expression::AttemptExpression(_) => panic!("Should have been removed in preprocessor"),
Expression::Literal(l) => {
MoveIRExpression::Literal(MoveLiteralToken { token: l }.generate())
Expand Down Expand Up @@ -140,7 +141,7 @@ impl MoveCastExpression {
expression: (*self.expression.expression).clone(),
position: Default::default(),
}
.generate(function_context);
.generate(function_context);

if original_type_information.0 <= target_type_information.0 {
return expression_code;
Expand Down Expand Up @@ -193,15 +194,16 @@ impl MoveSubscriptExpression {
expression: *index.clone(),
position: Default::default(),
}
.generate(function_context);
.generate(function_context);

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

let identifier_code = MoveIdentifier {
identifier,
position: self.position.clone(),
position: MovePosition::Left,
}
.generate(function_context, false, false);
.generate(function_context, false, true);

let base_type = function_context.environment.get_expression_type(
&Expression::Identifier(self.expression.base_expression.clone()),
&function_context.enclosing_type.clone(),
Expand All @@ -212,8 +214,35 @@ impl MoveSubscriptExpression {

if let MovePosition::Left = self.position.clone() {
return match base_type {
Type::FixedSizedArrayType(_) | Type::ArrayType(_) => {
MoveRuntimeFunction::append_to_array_int(identifier_code, rhs)
Type::FixedSizedArrayType(a) => {
let elem_type = MoveType::move_type(
*a.key_type,
Some(function_context.environment.clone()),
)
.generate(function_context);

MoveIRExpression::Assignment(MoveIRAssignment {
identifier: format!(
"*Vector.borrow_mut<{}>({}, {})",
elem_type, identifier_code, index
),
expression: Box::from(rhs),
})
}
Type::ArrayType(a) => {
let elem_type = MoveType::move_type(
*a.key_type,
Some(function_context.environment.clone()),
)
.generate(function_context);

MoveIRExpression::Assignment(MoveIRAssignment {
identifier: format!(
"*Vector.borrow_mut<{}>({}, {})",
elem_type, identifier_code, index
),
expression: Box::from(rhs),
})
}
Type::DictionaryType(_) => {
let f_name = format!(
Expand Down Expand Up @@ -242,15 +271,31 @@ impl MoveSubscriptExpression {
}

match base_type {
Type::FixedSizedArrayType(_) | Type::ArrayType(_) => {
let identifier = self.expression.base_expression.clone();

let identifier_code = MoveIdentifier {
identifier,
position: self.position.clone(),
}
.generate(function_context, false, true);
MoveRuntimeFunction::get_from_array_int(identifier_code, index)
Type::FixedSizedArrayType(a) => {
let identifier_code = MoveIRExpression::Operation(MoveIROperation::Reference(
Box::from(identifier_code),
));
let elem_type =
MoveType::move_type(*a.key_type, Some(function_context.environment.clone()))
.generate(function_context);

MoveIRExpression::Inline(format!(
"*Vector.borrow<{}>({}, {})",
elem_type, identifier_code, index
))
}
Type::ArrayType(a) => {
let identifier_code = MoveIRExpression::Operation(MoveIROperation::Reference(
Box::from(identifier_code),
));
let elem_type =
MoveType::move_type(*a.key_type, Some(function_context.environment.clone()))
.generate(function_context);

MoveIRExpression::Inline(format!(
"*Vector.borrow<{}>({}, {})",
elem_type, identifier_code, index
))
}
Type::DictionaryType(_) => {
let f_name = format!(
Expand Down Expand Up @@ -287,7 +332,7 @@ impl MoveInoutExpression {
expression: *self.expression.expression.clone(),
position: self.position.clone(),
}
.generate(function_context);
.generate(function_context);
}

if let MovePosition::Accessed = self.position {
Expand All @@ -298,7 +343,7 @@ impl MoveInoutExpression {
expression: *self.expression.expression.clone(),
position: MovePosition::Left,
}
.generate(function_context),
.generate(function_context),
)));
}
}
Expand All @@ -308,7 +353,7 @@ impl MoveInoutExpression {
expression: *self.expression.expression.clone(),
position: self.position.clone(),
}
.generate(function_context);
.generate(function_context);
}

let expression = self.expression.clone();
Expand All @@ -317,7 +362,7 @@ impl MoveInoutExpression {
expression: *expression.expression,
position: MovePosition::Inout,
}
.generate(function_context),
.generate(function_context),
)))
}
}
Expand All @@ -335,7 +380,7 @@ impl MoveBinaryExpression {
function_call: f,
module_name: "Self".to_string(),
}
.generate(function_context);
.generate(function_context);
}
return MovePropertyAccess {
left: *self.expression.lhs_expression.clone(),
Expand Down Expand Up @@ -450,10 +495,10 @@ pub fn is_signer_type(expression: &Expression, function_context: &FunctionContex
if let Some(identifier_type) = function_context.scope_context.type_for(&id.token) {
return identifier_type
== Type::UserDefinedType(Identifier {
token: MovePreProcessor::SIGNER_TYPE.to_string(),
enclosing_type: None,
line_info: Default::default(),
});
token: MovePreProcessor::SIGNER_TYPE.to_string(),
enclosing_type: None,
line_info: Default::default(),
});
}
}
false
Expand Down
4 changes: 2 additions & 2 deletions src/moveir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl MoveFunction {
identifier: p.identifier,
position: MovePosition::Left,
}
.generate(&function_context, false, false)
.generate(&function_context, false, false)
})
.collect();
let parameters: Vec<String> = parameters
Expand Down Expand Up @@ -189,7 +189,7 @@ impl MoveFunction {
identifier: id,
position: Default::default(),
}
.generate(&function_context, true, false);
.generate(&function_context, true, false);
function_context.emit(MoveIRStatement::Inline(format!("_ = {}", expression)));
}
}
Expand Down
1 change: 1 addition & 0 deletions src/moveir/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl MoveIdentifier {
if identifier_type.is_currency_type(&libra::currency()) {
return ir_identifier;
}

if identifier_type.is_inout_type() && identifier_type.is_user_defined_type() {
if f_call {
return MoveIRExpression::Transfer(MoveIRTransfer::Move(Box::from(
Expand Down
Loading