Skip to content

Commit

Permalink
lambda-compiler-completion
Browse files Browse the repository at this point in the history
  • Loading branch information
brmataptos committed Nov 25, 2024
1 parent cead217 commit 42421fe
Show file tree
Hide file tree
Showing 33 changed files with 6,417 additions and 651 deletions.
74 changes: 56 additions & 18 deletions third_party/move/move-compiler-v2/src/bytecode_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Parts of the project are originally copyright © Meta Platforms, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::{experiments::Experiment, Options};
use codespan_reporting::diagnostic::Severity;
use ethnum::U256;
use itertools::Itertools;
Expand Down Expand Up @@ -324,6 +325,14 @@ impl<'env> Generator<'env> {
let loc = env.get_node_loc(id);
env.diag(severity, &loc, msg.as_ref())
}

fn check_if_lambdas_enabled(&self) -> bool {
let options = self
.env()
.get_extension::<Options>()
.expect("Options is available");
options.experiment_on(Experiment::LAMBDA_VALUES)
}
}

// ======================================================================================
Expand Down Expand Up @@ -480,14 +489,23 @@ impl<'env> Generator<'env> {
self.emit_with(*id, |attr| Bytecode::SpecBlock(attr, spec));
},
// TODO(LAMBDA)
ExpData::Lambda(id, _, _, _, _) => self.error(
ExpData::Lambda(id, _, _, _, _) =>
self.error(
*id,
"Function-typed values not yet supported except as parameters to calls to inline functions",
if self.check_if_lambdas_enabled() {
"Function-typed values not yet implemented except as parameters to calls to inline functions"
} else {
"Function-typed values not yet supported except as parameters to calls to inline functions"
}
),
// TODO(LAMBDA)
ExpData::Invoke(id, _exp, _) => self.error(
*id,
"Calls to function values other than inline function parameters not yet supported",
if self.check_if_lambdas_enabled() {
"Calls to function values other than inline function parameters not yet implemented"
} else {
"Calls to function values other than inline function parameters not yet supported"
}
),
ExpData::Quant(id, _, _, _, _, _) => {
self.internal_error(*id, "unsupported specification construct")
Expand Down Expand Up @@ -564,10 +582,16 @@ impl<'env> Generator<'env> {
Constant::Bool(false)
}
},
// TODO(LAMBDA)
Value::Function(_mid, _fid) => {
self.error(
id,
"Function-typed values not yet supported except as parameters to calls to inline functions");
if self.check_if_lambdas_enabled() {
"Function-typed values not yet implemented except as parameters to calls to inline functions"
} else {
"Function-typed values not yet supported except as parameters to calls to inline functions"
}
);
Constant::Bool(false)
},
}
Expand Down Expand Up @@ -794,7 +818,11 @@ impl<'env> Generator<'env> {
// TODO(LAMBDA)
Operation::EarlyBind => self.error(
id,
"Function-typed values not yet supported except as parameters to calls to inline functions",
if self.check_if_lambdas_enabled() {
"Function-typed values not yet implemented except as parameters to calls to inline functions"
} else {
"Function-typed values not yet supported except as parameters to calls to inline functions"
},
),
Operation::TestVariants(mid, sid, variants) => {
self.gen_test_variants(targets, id, mid.qualified(*sid), variants, args)
Expand Down Expand Up @@ -1336,9 +1364,12 @@ impl<'env> Generator<'env> {
};
self.gen_borrow_field_operation(id, borrow_dest, str, fields, oper_temp);
if need_read_ref {
self.emit_call(id, vec![target], BytecodeOperation::ReadRef, vec![
borrow_dest,
])
self.emit_call(
id,
vec![target],
BytecodeOperation::ReadRef,
vec![borrow_dest],
)
}
}

Expand Down Expand Up @@ -1526,10 +1557,13 @@ enum MatchMode {
impl MatchMode {
/// Whether this match is in probing mode.
fn is_probing(&self) -> bool {
matches!(self, MatchMode::Refutable {
probing_vars: Some(_),
..
})
matches!(
self,
MatchMode::Refutable {
probing_vars: Some(_),
..
}
)
}

/// Whether a variable appearing in the pattern should be bound to a temporary.
Expand Down Expand Up @@ -1657,9 +1691,12 @@ impl<'env> Generator<'env> {
ReferenceKind::Immutable,
Box::new(value_ty.clone()),
));
self.emit_call(id, vec![value_ref], BytecodeOperation::BorrowLoc, vec![
value,
]);
self.emit_call(
id,
vec![value_ref],
BytecodeOperation::BorrowLoc,
vec![value],
);
needs_probing = true;
value_ref
}
Expand Down Expand Up @@ -1781,10 +1818,11 @@ impl<'env> Generator<'env> {
),
);
return Some(
ExpData::Call(id, Operation::Deref, vec![ExpData::LocalVar(
new_id, var,
ExpData::Call(
id,
Operation::Deref,
vec![ExpData::LocalVar(new_id, var).into_exp()],
)
.into_exp()])
.into_exp(),
);
}
Expand Down
78 changes: 61 additions & 17 deletions third_party/move/move-compiler-v2/src/env_pipeline/lambda_lifter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
//! not modify free variables.
//!
//! Lambda lifting rewrites lambda expressions into construction
//! of *closures*. A closure refers to a function and contains a partial list
//! of arguments for that function, essentially currying it. We use the
//! of *closures* using the `EarlyBind` operation. A closure refers to a function and contains a list
//! of "early bound" leading arguments for that function, essentially currying it. We use the
//! `EarlyBind` operation to construct a closure from a function and set of arguemnts,
//! which must be the first `k` arguments to the function argument list.
//!
Expand Down Expand Up @@ -204,7 +204,9 @@ impl<'a> LambdaLifter<'a> {
/// - `closure_args` = corresponding expressions to provide as actual arg for each param
/// - `param_index_mapping` = for each free var which is a Parameter from the enclosing function,
/// a mapping from index there to index in the params list
fn get_params_for_freevars(&mut self) -> (Vec<Parameter>, Vec<Exp>, BTreeMap<usize, usize>) {
fn get_params_for_freevars(
&mut self,
) -> Option<(Vec<Parameter>, Vec<Exp>, BTreeMap<usize, usize>)> {
let env = self.fun_env.module_env.env;
let mut closure_args = vec![];

Expand All @@ -213,6 +215,9 @@ impl<'a> LambdaLifter<'a> {
// functions (courtesy of #12317)
let mut param_index_mapping = BTreeMap::new();
let mut params = vec![];
let ty_params = self.fun_env.get_type_parameters_ref();
let ability_inferer = AbilityInferer::new(env, ty_params);
let mut saw_error = false;

for (used_param_count, (param, var_info)) in
mem::take(&mut self.free_params).into_iter().enumerate()
Expand All @@ -229,6 +234,18 @@ impl<'a> LambdaLifter<'a> {
name.display(env.symbol_pool())
),
);
saw_error = true;
}
let param_abilities = ability_inferer.infer_abilities(&ty).1;
if !param_abilities.has_copy() {
env.error(
&loc,
&format!(
"captured variable `{}` must have a value with `copy` ability", // TODO(LAMBDA)
name.display(env.symbol_pool())
),
);
saw_error = true;
}
params.push(Parameter(name, ty.clone(), loc.clone()));
let new_id = env.new_node(loc, ty);
Expand All @@ -252,6 +269,7 @@ impl<'a> LambdaLifter<'a> {
name.display(env.symbol_pool())
),
);
saw_error = true;
}
params.push(Parameter(name, ty.clone(), loc.clone()));
let new_id = env.new_node(loc, ty);
Expand All @@ -261,7 +279,11 @@ impl<'a> LambdaLifter<'a> {
closure_args.push(ExpData::LocalVar(new_id, name).into_exp())
}

(params, closure_args, param_index_mapping)
if !saw_error {
Some((params, closure_args, param_index_mapping))
} else {
None
}
}

fn get_arg_if_simple(arg: &Exp) -> Option<&Exp> {
Expand Down Expand Up @@ -606,10 +628,13 @@ impl<'a> ExpRewriterFunctions for LambdaLifter<'a> {

fn rewrite_assign(&mut self, _node_id: NodeId, lhs: &Pattern, _rhs: &Exp) -> Option<Exp> {
for (node_id, name) in lhs.vars() {
self.free_locals.insert(name, VarInfo {
node_id,
modified: true,
});
self.free_locals.insert(
name,
VarInfo {
node_id,
modified: true,
},
);
}
None
}
Expand All @@ -618,16 +643,22 @@ impl<'a> ExpRewriterFunctions for LambdaLifter<'a> {
if matches!(oper, Operation::Borrow(ReferenceKind::Mutable)) {
match args[0].as_ref() {
ExpData::LocalVar(node_id, name) => {
self.free_locals.insert(*name, VarInfo {
node_id: *node_id,
modified: true,
});
self.free_locals.insert(
*name,
VarInfo {
node_id: *node_id,
modified: true,
},
);
},
ExpData::Temporary(node_id, param) => {
self.free_params.insert(*param, VarInfo {
node_id: *node_id,
modified: true,
});
self.free_params.insert(
*param,
VarInfo {
node_id: *node_id,
modified: true,
},
);
},
_ => {},
}
Expand Down Expand Up @@ -669,7 +700,11 @@ impl<'a> ExpRewriterFunctions for LambdaLifter<'a> {
// param_index_mapping = for each free var which is a Parameter from the enclosing function,
// a mapping from index there to index in the params list; other free vars are
// substituted automatically by using the same symbol for the param
let (mut params, mut closure_args, param_index_mapping) = self.get_params_for_freevars();
let Some((mut params, mut closure_args, param_index_mapping)) =
self.get_params_for_freevars()
else {
return None;
};

// Some(ExpData::Invalid(env.clone_node(id)).into_exp());
// Add lambda args. For dealing with patterns in lambdas (`|S{..}|e`) we need
Expand Down Expand Up @@ -732,6 +767,15 @@ impl<'a> ExpRewriterFunctions for LambdaLifter<'a> {
let body = ExpRewriter::new(env, &mut replacer).rewrite_exp(body.clone());
let fun_id = FunId::new(fun_name);
let params_types = params.iter().map(|param| param.get_type()).collect();
if abilities.has_store() {
let loc = env.get_node_loc(id);
env.error(
&loc,
// TODO(LAMBDA)
"Lambdas expressions with `store` ability currently may only be a simple call to an existing `public` function. This lambda expression requires defining a `public` helper function, which might affect module upgradeability and is not yet supported."
);
return None;
};
self.lifted.push(ClosureFunction {
loc: lambda_loc.clone(),
fun_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ impl ModuleGenerator {
ctx.error(
loc,
format!(
"Unexpected type: {}",
"Unimplemented type: {}",
ty.display(&ctx.env.get_type_display_ctx())
),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Diagnostics:
warning: Unused parameter `f`. Consider removing or prefixing with an underscore: `_f`
┌─ tests/checking/inlining/function_name_shadowing.move:8:28
8 │ public inline fun quux(f:|u64, u64|u64, a: u64, b: u64): u64 {
│ ^

// -- Model dump before bytecode pipeline
module 0x42::Test {
public fun f(a: u64,b: u64): u64 {
Mul<u64>(a, b)
}
public inline fun quux(f: |(u64, u64)|u64,a: u64,b: u64): u64 {
Test::f(a, b)
}
public fun test_shadowing(): u64 {
Test::f(10, 2)
}
} // end 0x42::Test

// -- Sourcified model before bytecode pipeline
module 0x42::Test {
public fun f(a: u64, b: u64): u64 {
a * b
}
public inline fun quux(f: |(u64, u64)|u64, a: u64, b: u64): u64 {
f(a, b)
}
public fun test_shadowing(): u64 {
f(10, 2)
}
}


============ bytecode verification succeeded ========
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//# publish
module 0x42::Test {

public fun f(a: u64, b: u64): u64 {
a * b
}

public inline fun quux(f:|u64, u64|u64, a: u64, b: u64): u64 {
f(a, b)
}

public fun test_shadowing(): u64 {
quux(|a, b| a - b, 10, 2)
}
}

//# run 0x42::Test::test_shadowing
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,3 @@ error: tuple type `()` is not allowed as a type argument (type was inferred)
│ ^
= required by instantiating type parameter `T` of function `foreach`

error: function type `|u64|u64` is not allowed as a field type
┌─ tests/checking/typing/lambda.move:81:12
81 │ f: |u64|u64, // expected lambda not allowed
│ ^^^^^^^^
= required by declaration of field `f`
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,3 @@ error: cannot pass `|&u64|u64 with copy+drop+store` to a function which expects
73 │ foreach(&v, |e: &u64| { sum = sum + *e; *e }) // expected to have wrong result type of lambda
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: function type `|u64|u64` is not allowed as a field type
┌─ tests/checking/typing/lambda_typed.move:81:12
81 │ f: |u64|u64, // expected lambda not allowed
│ ^^^^^^^^
= required by declaration of field `f`
Original file line number Diff line number Diff line change
Expand Up @@ -532,13 +532,13 @@ module 0x8675309::M {


Diagnostics:
error: Calls to function values other than inline function parameters not yet supported
error: Calls to function values other than inline function parameters not yet implemented
┌─ tests/lambda/inline-parity/subtype_args.move:24:9
24 │ f(&mut 0, &mut 0);
│ ^^^^^^^^^^^^^^^^^

error: Calls to function values other than inline function parameters not yet supported
error: Calls to function values other than inline function parameters not yet implemented
┌─ tests/lambda/inline-parity/subtype_args.move:25:9
25 │ f(&0, &mut 0);
Expand Down
Loading

0 comments on commit 42421fe

Please sign in to comment.