-
Notifications
You must be signed in to change notification settings - Fork 11.3k
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
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
3 Ignored Deployments
|
There was a problem hiding this 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!(), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
if next_tok != Tok::LBrace | ||
&& next_tok != Tok::Less | ||
&& next_tok != Tok::ColonColon | ||
&& next_tok != Tok::LParen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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() { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) | ||
} | ||
_ => { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 None
s 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() { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. Will remove
28 │ let Foo() = x; | ||
│ ^^^^^ Missing binding for field '0' in '0x42::M::Foo' |
There was a problem hiding this comment.
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` |
There was a problem hiding this comment.
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 not
0_u8`?
error[E03010]: unbound field | ||
┌─ tests/move_2024/parser/positional_field_access_no_annotations.move:6:9 | ||
│ | ||
6 │ y.0_u8 |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this 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 :)
708c748
to
db37b4a
Compare
Thanks for the comments! Should be updated now. Added tests for the explicit type arguments. Note however, that for unpacks things like |
But not accepted at unpack assignment? |
Oh? But I removed that nonsense I think (It was a thing long ago) |
There was a problem hiding this 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!
We could also maybe check that if all fields are |
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 :) |
db37b4a
to
91a9944
Compare
91a9944
to
241a88f
Compare
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
Expansion
Call
toUnpack
if it's in anLValue
position (similar to what we do in expansion forPack
toUnpack
in LValue position).Naming
Call
s are disambiguated intoPacks
or remain asCall
s based on the name references an in-scope struct type.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)
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)
).