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

[move-compiler] Add positional struct fields #14073

Merged
merged 2 commits into from
Oct 5, 2023
Merged

Conversation

tzakian
Copy link
Contributor

@tzakian tzakian commented Oct 3, 2023

Description

Adds support for positional struct fields in Move 2024.

This affects the parser, expansion, and naming (and a minor update is needed in to_bytecode).

Parser

  • Updated to support positional struct declarations
  • Updated dotted field accesses to support valid struct indices
  • Updated binding parsing to support positional struct destructuring

Expansion

  • Added support for conversion of a Call to Unpack if it's in an LValue position (similar to what we do in expansion for Pack to Unpack in LValue position).

Naming

  • Positional fields are removed at this point
  • Calls are disambiguated into Packs or remain as Calls based on the name references an in-scope struct type.
  • We enforce that named packs/unpacks and positional packs/unpacks correspond to a named/positional struct declaration.

Test Plan

Added a number of tests for each phase.


If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process.

Type of Change (Check all that apply)

  • protocol change
  • user-visible impact
  • breaking change for a client SDKs
  • breaking change for FNs (FN binary must upgrade)
  • breaking change for validators or node operators (must upgrade binaries)
  • breaking change for on-chain data layout
  • necessitate either a data wipe or data migration

Release notes

Adds positional struct declaration support to Move 2024.alpha. When using the 2024 edition you'll be able to declare structs with positional fields (e.g., public struct Positional(u64, bool)), access positional struct fields via their index (e.g., x.0 ,x.1) and unpack/pack them positionally (e.g., let Positional(value, is_tf) = x;, let x = Positional(0, true)).

@tzakian tzakian requested review from cgswords and tnowacki October 3, 2023 17:26
@vercel
Copy link

vercel bot commented Oct 3, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mysten-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 5, 2023 6:54pm
sui-typescript-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 5, 2023 6:54pm
3 Ignored Deployments
Name Status Preview Comments Updated (UTC)
explorer ⬜️ Ignored (Inspect) Visit Preview Oct 5, 2023 6:54pm
multisig-toolkit ⬜️ Ignored (Inspect) Visit Preview Oct 5, 2023 6:54pm
sui-kiosk ⬜️ Ignored (Inspect) Visit Preview Oct 5, 2023 6:54pm

Copy link
Contributor

@tnowacki tnowacki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I missed them, but are there tests with explicit type args? e.g. Cup<u64>(0)

My usual rundown for those would be

  • Pack
  • Unpack
  • Pack with space (should fail), e.g. Cup <u64>(0)
  • Unpack with space (should fail), e.g. Cup <u64>(0)

st.ast_debug(w);
});
}),
StructFields::Native(_) => unreachable!(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be unreachable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point -- should just do nothing. Will fix

Comment on lines 664 to 667
if next_tok != Tok::LBrace
&& next_tok != Tok::Less
&& next_tok != Tok::ColonColon
&& next_tok != Tok::LParen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if next_tok != Tok::LBrace
&& next_tok != Tok::Less
&& next_tok != Tok::ColonColon
&& next_tok != Tok::LParen
if !matches!(next_tok, Tok::LBrace | Tok::Less | Tok::ColonColon | Tok::LParen) {

) =>
{
let contents = context.tokens.content();
if parse_u8(contents).is_err() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is the current max number of fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have a hard limit in the compiler, but the bytecode verifier will cap these at 200, so bounding it to 255 seems like a nice bound.

let field_access = Name::new(loc, contents.into());
Exp_::Dot(Box::new(lhs), field_access)
}
_ => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do matches above if you want to unindent one level. This is fine too though :)

E::FieldBindings::Named(fields)
}
FieldBindings::Positional(positional_bindings) => {
let fields: Option<Vec<E::LValue>> = positional_bindings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good for now, Just wondering if we should maybe replace Nones on each inner binding with some sort of UnresolvedError binding

@@ -787,7 +787,13 @@ fn var(v: Var) -> IR::Var {
}

fn field(f: Field) -> IR::Field {
sp(f.0.loc, IR::Field_(f.0.value))
// If it's a positional field, lower it into `pos{field_idx}` so they're a valid identifier
let field_ident = if f.0.value.parse::<u8>().is_ok() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is f.0.value.starts_with('0'..'9') not sufficient?

This seems fine... just something about parsing it scares me lol

@@ -0,0 +1,8 @@
error[E13001]: feature is not supported in specified edition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all of these parser tests duplicated for legacy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. Will remove

Comment on lines +182 to +183
28 │ let Foo() = x;
│ ^^^^^ Missing binding for field '0' in '0x42::M::Foo'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These errors are mildly hilarious, but good enough for now

6 │ y.0_u8
│ ^^^^ Invalid field access. Expected a number less than or equal to 255
= Positional fields must be in the range [0 .. 255] and not be typed, e.g. `0_u8`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be , e.g. 0but not0_u8`?

error[E03010]: unbound field
┌─ tests/move_2024/parser/positional_field_access_no_annotations.move:6:9
6 │ y.0_u8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test for y.1_0 instead of y.10?
What about leading zeros? y.0001?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not. But I will add them. They should "just work" since the field parsing for these is the same function that we use to parse numbers normally.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we support this though? It feels a bit ... strange? Or maybe we just say fuck it and have the autoformatter replace them to something sane?

Copy link
Contributor

@tnowacki tnowacki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this! And glad by the looks of it turned out to be relatively easy :)

@tzakian
Copy link
Contributor Author

tzakian commented Oct 4, 2023

Thanks for the comments! Should be updated now. Added tests for the explicit type arguments.

Note however, that for unpacks things like let Foo <u64>(x) = ... are accepted, but this is in-line with what we currently accept for named unpacks today -- let Bar <u64> { x } = ... is also accepted.

@tnowacki
Copy link
Contributor

tnowacki commented Oct 4, 2023

Thanks for the comments! Should be updated now. Added tests for the explicit type arguments.

Note however, that for unpacks things like let Foo <u64>(x) = ... are accepted, but this is in-line with what we currently accept for named unpacks today -- let Bar <u64> { x } = ... is also accepted.

But not accepted at unpack assignment? Foo <u64>(x) = e?

@tnowacki
Copy link
Contributor

tnowacki commented Oct 4, 2023

Oh? But I removed that nonsense I think (It was a thing long ago)

Copy link
Contributor

@tnowacki tnowacki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉
This has been on my todo list since the first version of the compiler. So thanks!

@tnowacki
Copy link
Contributor

tnowacki commented Oct 4, 2023

We could also maybe check that if all fields are pos0...posn with no gaps, that we convert back to positional, when doing the interface generator. But also fine if we don't do that :)

@tzakian
Copy link
Contributor Author

tzakian commented Oct 5, 2023

I think it's fine if we don't do it for interface generation (at least until we have public struct fields) since those fields aren't publicly visible so I'll leave it as-is :)

@tzakian tzakian enabled auto-merge (squash) October 5, 2023 15:17
@tzakian tzakian force-pushed the tzakian/positional-structs branch from db37b4a to 91a9944 Compare October 5, 2023 15:26
@tzakian tzakian force-pushed the tzakian/positional-structs branch from 91a9944 to 241a88f Compare October 5, 2023 18:53
@vercel vercel bot temporarily deployed to Preview – mysten-ui October 5, 2023 18:54 Inactive
@tzakian tzakian merged commit ff37443 into main Oct 5, 2023
@tzakian tzakian deleted the tzakian/positional-structs branch October 5, 2023 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants