Skip to content

Commit 1f72142

Browse files
authored
[move-compiler] Add support for postfix ability declarations (#13782)
## Description Adds support for postfix ability declarations to the source language in the move2024 edition. ## Test Plan Added a number of test for both pre-move2024 and move2024.
1 parent ab04458 commit 1f72142

File tree

61 files changed

+573
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+573
-13
lines changed

move-compiler/src/editions/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub struct Edition {
2828
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
2929
pub enum FeatureGate {
3030
PublicPackage,
31+
PostFixAbilities,
3132
}
3233

3334
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord, Default)]
@@ -41,7 +42,12 @@ pub enum Flavor {
4142
// Entry
4243
//**************************************************************************************************
4344

44-
pub fn check_feature(env: &mut CompilationEnv, edition: Edition, feature: &FeatureGate, loc: Loc) {
45+
pub fn check_feature(
46+
env: &mut CompilationEnv,
47+
edition: Edition,
48+
feature: &FeatureGate,
49+
loc: Loc,
50+
) -> bool {
4551
if !edition.supports(feature) {
4652
let valid_editions = valid_editions_for_feature(feature)
4753
.into_iter()
@@ -63,6 +69,9 @@ pub fn check_feature(env: &mut CompilationEnv, edition: Edition, feature: &Featu
6369
or via command line flag if invoking the compiler directly.",
6470
);
6571
env.add_diag(diag);
72+
false
73+
} else {
74+
true
6675
}
6776
}
6877

@@ -81,6 +90,9 @@ pub fn valid_editions_for_feature(feature: &FeatureGate) -> Vec<Edition> {
8190
static SUPPORTED_FEATURES: Lazy<BTreeMap<Edition, BTreeSet<FeatureGate>>> =
8291
Lazy::new(|| BTreeMap::from_iter(Edition::ALL.iter().map(|e| (*e, e.features()))));
8392

93+
const E2024_ALPHA_FEATURES: &[FeatureGate] =
94+
&[FeatureGate::PublicPackage, FeatureGate::PostFixAbilities];
95+
8496
impl Edition {
8597
pub const LEGACY: Self = Self {
8698
edition: symbol!("legacy"),
@@ -115,7 +127,7 @@ impl Edition {
115127
Self::LEGACY => BTreeSet::new(),
116128
Self::E2024_ALPHA => {
117129
let mut features = self.prev().unwrap().features();
118-
features.extend([FeatureGate::PublicPackage]);
130+
features.extend(E2024_ALPHA_FEATURES);
119131
features
120132
}
121133
_ => self.unknown_edition_panic(),

move-compiler/src/parser/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ pub(crate) fn parse_program(
7575
named_address_map,
7676
} in targets
7777
{
78-
let (defs, comments, ds, file_hash) = parse_file(compilation_env, &mut files, path)?;
78+
let (defs, comments, ds, file_hash) =
79+
parse_file(compilation_env, &mut files, path, package)?;
7980
source_definitions.extend(defs.into_iter().map(|def| PackageDefinition {
8081
package,
8182
named_address_map,
@@ -91,7 +92,7 @@ pub(crate) fn parse_program(
9192
named_address_map,
9293
} in deps
9394
{
94-
let (defs, _, ds, _) = parse_file(compilation_env, &mut files, path)?;
95+
let (defs, _, ds, _) = parse_file(compilation_env, &mut files, path, package)?;
9596
lib_definitions.extend(defs.into_iter().map(|def| PackageDefinition {
9697
package,
9798
named_address_map,
@@ -164,6 +165,7 @@ fn parse_file(
164165
compilation_env: &mut CompilationEnv,
165166
files: &mut FilesSourceText,
166167
fname: Symbol,
168+
package: Option<Symbol>,
167169
) -> anyhow::Result<(
168170
Vec<parser::ast::Definition>,
169171
MatchedFileCommentMap,
@@ -184,7 +186,7 @@ fn parse_file(
184186
}
185187
Ok(()) => &source_buffer,
186188
};
187-
let (defs, comments) = match parse_file_string(compilation_env, file_hash, buffer) {
189+
let (defs, comments) = match parse_file_string(compilation_env, file_hash, buffer, package) {
188190
Ok(defs_and_comments) => defs_and_comments,
189191
Err(ds) => {
190192
diags.extend(ds);

move-compiler/src/parser/syntax.rs

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,29 @@ use move_symbol_pool::{symbol, Symbol};
1313
use crate::{
1414
diag,
1515
diagnostics::{Diagnostic, Diagnostics},
16+
editions::FeatureGate,
1617
parser::{ast::*, lexer::*},
1718
shared::*,
1819
MatchedFileCommentMap,
1920
};
2021

2122
struct Context<'env, 'lexer, 'input> {
23+
package_name: Option<Symbol>,
2224
env: &'env mut CompilationEnv,
2325
tokens: &'lexer mut Lexer<'input>,
2426
}
2527

2628
impl<'env, 'lexer, 'input> Context<'env, 'lexer, 'input> {
27-
fn new(env: &'env mut CompilationEnv, tokens: &'lexer mut Lexer<'input>) -> Self {
28-
Self { env, tokens }
29+
fn new(
30+
env: &'env mut CompilationEnv,
31+
tokens: &'lexer mut Lexer<'input>,
32+
package_name: Option<Symbol>,
33+
) -> Self {
34+
Self {
35+
package_name,
36+
env,
37+
tokens,
38+
}
2939
}
3040
}
3141

@@ -1995,9 +2005,11 @@ fn parse_parameter(context: &mut Context) -> Result<(Var, Type), Box<Diagnostic>
19952005
// Parse a struct definition:
19962006
// StructDecl =
19972007
// "struct" <StructDefName> ("has" <Ability> (, <Ability>)+)?
1998-
// ("{" Comma<FieldAnnot> "}" | ";")
2008+
// ("{" Comma<FieldAnnot> "}" ("has" <Ability> (, <Ability>)+;)? | ";")
19992009
// StructDefName =
20002010
// <Identifier> <OptionalTypeParameters>
2011+
// Where the the two "has" statements are mutually exclusive -- a struct cannot be declared with
2012+
// both infix and postfix ability declarations.
20012013
fn parse_struct_decl(
20022014
attributes: Vec<Attributes>,
20032015
start_loc: usize,
@@ -2035,8 +2047,13 @@ fn parse_struct_decl(
20352047
let name = StructName(parse_identifier(context)?);
20362048
let type_parameters = parse_struct_type_parameters(context)?;
20372049

2038-
let abilities = if context.tokens.peek() == Tok::Identifier && context.tokens.content() == "has"
2039-
{
2050+
let infix_ability_declaration_loc =
2051+
if context.tokens.peek() == Tok::Identifier && context.tokens.content() == "has" {
2052+
Some(current_token_loc(context.tokens))
2053+
} else {
2054+
None
2055+
};
2056+
let mut abilities = if infix_ability_declaration_loc.is_some() {
20402057
context.tokens.advance()?;
20412058
parse_list(
20422059
context,
@@ -2075,6 +2092,11 @@ fn parse_struct_decl(
20752092
parse_field_annot,
20762093
"a field",
20772094
)?;
2095+
parse_postfix_ability_declarations(
2096+
infix_ability_declaration_loc,
2097+
&mut abilities,
2098+
context,
2099+
)?;
20782100
StructFields::Defined(list)
20792101
}
20802102
};
@@ -2104,6 +2126,63 @@ fn parse_field_annot(context: &mut Context) -> Result<(Field, Type), Box<Diagnos
21042126
Ok((f, st))
21052127
}
21062128

2129+
// Parse a postfix ability declaration:
2130+
// "has" <Ability> (, <Ability>)+;
2131+
// Error if:
2132+
// * Also has prefix ability declaration
2133+
fn parse_postfix_ability_declarations(
2134+
infix_ability_declaration_loc: Option<Loc>,
2135+
abilities: &mut Vec<Ability>,
2136+
context: &mut Context,
2137+
) -> Result<(), Box<Diagnostic>> {
2138+
let postfix_ability_declaration =
2139+
context.tokens.peek() == Tok::Identifier && context.tokens.content() == "has";
2140+
let has_location = current_token_loc(context.tokens);
2141+
2142+
if postfix_ability_declaration {
2143+
context.env.check_feature(
2144+
&FeatureGate::PostFixAbilities,
2145+
context.package_name,
2146+
has_location,
2147+
);
2148+
2149+
context.tokens.advance()?;
2150+
2151+
// Only add a diagnostic about prefix xor postfix ability declarations if the feature is
2152+
// supported. Otherwise we will already have an error that the `has` is not supported in
2153+
// that position, and the feature check diagnostic as well, so adding this additional error
2154+
// could be confusing.
2155+
if let Some(previous_declaration_loc) = infix_ability_declaration_loc {
2156+
let msg = "Duplicate ability declaration. Abilities can be declared before \
2157+
or after the field declarations, but not both.";
2158+
let prev_msg = "Ability declaration previously given here";
2159+
context.env.add_diag(diag!(
2160+
Syntax::InvalidModifier,
2161+
(has_location, msg),
2162+
(previous_declaration_loc, prev_msg)
2163+
));
2164+
}
2165+
2166+
*abilities = parse_list(
2167+
context,
2168+
|context| match context.tokens.peek() {
2169+
Tok::Comma => {
2170+
context.tokens.advance()?;
2171+
Ok(true)
2172+
}
2173+
Tok::Semicolon => Ok(false),
2174+
_ => Err(unexpected_token_error(
2175+
context.tokens,
2176+
&format!("one of: '{}' or '{}'", Tok::Comma, Tok::Semicolon),
2177+
)),
2178+
},
2179+
parse_ability,
2180+
)?;
2181+
consume_token(context.tokens, Tok::Semicolon)?;
2182+
}
2183+
Ok(())
2184+
}
2185+
21072186
//**************************************************************************************************
21082187
// Constants
21092188
//**************************************************************************************************
@@ -3226,13 +3305,14 @@ pub fn parse_file_string(
32263305
env: &mut CompilationEnv,
32273306
file_hash: FileHash,
32283307
input: &str,
3308+
package: Option<Symbol>,
32293309
) -> Result<(Vec<Definition>, MatchedFileCommentMap), Diagnostics> {
32303310
let mut tokens = Lexer::new(input, file_hash);
32313311
match tokens.advance() {
32323312
Err(err) => Err(Diagnostics::from(vec![*err])),
32333313
Ok(..) => Ok(()),
32343314
}?;
3235-
match parse_file(&mut Context::new(env, &mut tokens)) {
3315+
match parse_file(&mut Context::new(env, &mut tokens, package)) {
32363316
Err(err) => Err(Diagnostics::from(vec![*err])),
32373317
Ok(def) => Ok((def, tokens.check_and_get_doc_comments(env))),
32383318
}

move-compiler/src/shared/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,14 @@ impl CompilationEnv {
523523
self.visitors.clone()
524524
}
525525

526-
// Logs an error if the feature isn't supported.
527-
pub fn check_feature(&mut self, feature: &FeatureGate, package: Option<Symbol>, loc: Loc) {
526+
// Logs an error if the feature isn't supported. Returns `false` if the feature is not
527+
// supported, and `true` otherwise.
528+
pub fn check_feature(
529+
&mut self,
530+
feature: &FeatureGate,
531+
package: Option<Symbol>,
532+
loc: Loc,
533+
) -> bool {
528534
edition_check_feature(self, self.package_config(package).edition, feature, loc)
529535
}
530536

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error[E01003]: invalid modifier
2+
┌─ tests/move_2024/parser/ability_modifier_infix_and_postfix.move:4:34
3+
4+
4 │ struct Foo has copy, drop {} has store;
5+
│ --- ^^^ Duplicate ability declaration. Abilities can be declared before or after the field declarations, but not both.
6+
│ │
7+
│ Ability declaration previously given here
8+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
address 0x42 {
2+
module M {
3+
// has both prefix and postfix ability declarations
4+
struct Foo has copy, drop {} has store;
5+
}
6+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E01002]: unexpected token
2+
┌─ tests/move_2024/parser/ability_modifier_infix_and_postfix_native_struct.move:5:39
3+
4+
5 │ native struct Foo has copy, drop; has store;
5+
│ ^^^
6+
│ │
7+
│ Unexpected 'has'
8+
│ Expected a module member: 'spec', 'use', 'friend', 'const', 'fun', or 'struct'
9+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
address 0x42 {
2+
module M {
3+
// has both invalid declaration since postfix ability declarations
4+
// are not allowed for native structs
5+
native struct Foo has copy, drop; has store;
6+
}
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E01002]: unexpected token
2+
┌─ tests/move_2024/parser/ability_modifier_infix_postfix_no_fields.move:4:38
3+
4+
4 │ native struct Foo has copy, drop has store;
5+
│ ^^^
6+
│ │
7+
│ Unexpected 'has'
8+
│ Expected one of: ',', '{', or ';'
9+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
address 0x42 {
2+
module M {
3+
// has both prefix and invalid postfix ability declarations
4+
native struct Foo has copy, drop has store;
5+
}
6+
}

0 commit comments

Comments
 (0)