Skip to content

Commit

Permalink
[compiler-v2] Pattern matching for struct variants (aka enum types)
Browse files Browse the repository at this point in the history
This implements the Move 2 `match` expression on stackless bytecode level. The PR consists of the following parts:

1. *Extending the Bytecode*

There are a few new bytecode operations:

```
dest := TestVariant(struct_id, variant, src)
dest := PackVariant(struct_id, variant, srcs)
dests := UnpackVariant(struct_id, variant, src)
dest := BorrowFieldVariant(struct_id, variant, field, src)
```

The `TestVariant` and `BorrowFieldVariant` operations  expect references to the variant struct. The
`UnpackVariant` and `BorrowFieldVariant` operations abort if the `src` value is not a value of
the given variant.

Notably, there are no new control flow operations, and the remaining part of the stackless bytecode
framework should operate without changes. This may change in the future if we introduce a new
branch instruction for switching over variants.

2. *Translating Matches*

Matches are translated into cascades of test & branch instructions. The translation is complicated by
the need for 'probing' whether a value can be matched before it is consumed. See for discussion in the
code how this problem is solved.

The current translation is manually reviewed via the baseline output, but it will not be fully tested
before we are able to run e2e tests with VM execution. (Future PRs.)

3. *Checking Match Coverage*

Even though we generate robust match code which is resilient against addition of new match variants
by explicit `abort` if there is no match, at compile time complete match coverage is required.
Checking for this is non-trivial because of the sequential, imperative semantics of matching.
See documentation in the code.

4. *Other*

There are some only indirectlyu related changes in this PR. To implement non-destructive matches
with conditions, it appeared handy to have `*&x` to be equivalent to `x`. This seems to be general
useful and is AFAICT also in Rust.

Also, the new pattern translation is a bit more efficient also in the `let` case, leading to some changes in baseline files.
  • Loading branch information
wrwg committed Jun 20, 2024
1 parent 97a555b commit 1cb5772
Show file tree
Hide file tree
Showing 26 changed files with 2,810 additions and 1,036 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
processed 4 tasks
6 changes: 6 additions & 0 deletions third_party/move/evm/move-to-yul/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,12 @@ impl<'a> FunctionGenerator<'a> {

// Unimplemented
Vector => unimplemented!("vector"),
TestVariant(..)
| PackVariant(..)
| UnpackVariant(..)
| BorrowFieldVariant(..) => {
unimplemented!("variants")

Check warning on line 555 in third_party/move/evm/move-to-yul/src/functions.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/evm/move-to-yul/src/functions.rs#L555

Added line #L555 was not covered by tests
},

// Specification or other operations which can be ignored here
Release
Expand Down
941 changes: 828 additions & 113 deletions third_party/move/move-compiler-v2/src/bytecode_generator.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,12 @@ impl<'a> FunctionGenerator<'a> {
self.flush_any_conflicts(ctx, dest, source);
let fun_ctx = ctx.fun_ctx;
match oper {
Operation::TestVariant(..)
| Operation::PackVariant(..)
| Operation::UnpackVariant(..)
| Operation::BorrowFieldVariant(..) => {
fun_ctx.internal_error("variants not yet implemented")
},
Operation::Function(mid, fid, inst) => {
self.gen_call(ctx, dest, mid.qualified(*fid), inst, source);
},
Expand Down Expand Up @@ -992,7 +998,10 @@ impl<'a> FunctionGenerator<'a> {
impl<'env> FunctionContext<'env> {
/// Emits an internal error for this function.
pub fn internal_error(&self, msg: impl AsRef<str>) {
self.module.internal_error(&self.loc, msg)
self.module.internal_error(
&self.loc,
format!("file format generator: {}", msg.as_ref()),
)
}

/// Gets the type of the temporary.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@

Diagnostics:
error: value of type `M::S<signer>` does not have the `copy` ability
┌─ tests/ability-check/v1-signer/read_ref_transitive.move:5:9
5 │ *&x
│ ^^^ reference content copied here

error: value of type `signer` does not have the `copy` ability
┌─ tests/ability-check/v1-signer/read_ref_transitive.move:15:9
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ error: value of type `M::R` does not have the `drop` ability
┌─ tests/ability-check/v1-typing/assign_pop_resource.move:5:9
5 │ _ = R{};
│ ^^^^^^^ implicitly dropped here since it is no longer used
│ ^ implicitly dropped here since it is no longer used

error: value of type `M::R` does not have the `drop` ability
┌─ tests/ability-check/v1-typing/assign_pop_resource.move:6:9
┌─ tests/ability-check/v1-typing/assign_pop_resource.move:6:10
6 │ (_, _) = (R{}, R{});
│ ^^^^^^^^^^^^^^^^^^^ implicitly dropped here since it is no longer used
│ ^ implicitly dropped here since it is no longer used

error: value of type `M::R` does not have the `drop` ability
┌─ tests/ability-check/v1-typing/assign_pop_resource.move:6:13
6 │ (_, _) = (R{}, R{});
│ ^ implicitly dropped here since it is no longer used
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ error: local `_r` of type `M::R` does not have the `drop` ability
│ ^^^ implicitly dropped here since it is no longer used

error: value of type `M::R` does not have the `drop` ability
┌─ tests/ability-check/v1-typing/bind_pop_resource.move:9:13
┌─ tests/ability-check/v1-typing/bind_pop_resource.move:9:14
9 │ let (_, _):(R, R) = (R{}, R{});
│ ^^^^^^ implicitly dropped here since it is no longer used
│ ^ implicitly dropped here since it is no longer used

error: value of type `M::R` does not have the `drop` ability
┌─ tests/ability-check/v1-typing/bind_pop_resource.move:9:17
9 │ let (_, _):(R, R) = (R{}, R{});
│ ^ implicitly dropped here since it is no longer used
Loading

0 comments on commit 1cb5772

Please sign in to comment.