Skip to content

Commit a86c40c

Browse files
cgswordstzakiantnowacki
authored
[move-compiler] add support for public(package) visibility. (#13408)
## Description This adds support for `public(package)` visibility, including feature gating it between Move 2024 edition. It also includes tests to ensure visibility and feature gate checking. ## Test Plan Add new tests to the compiler, including expected results. ### Type of Change (Check all that apply) - [ ] protocol change - [x] 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 Initial `public(package)` support added to Move 2024.alpha. This compiler feature is intended to be an easier to use variant of `public(friend)` --------- Co-authored-by: Tim Zakian <[email protected]> Co-authored-by: Todd Nowacki <[email protected]>
1 parent c5afbd9 commit a86c40c

File tree

58 files changed

+810
-66
lines changed

Some content is hidden

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

58 files changed

+810
-66
lines changed

move-compiler/src/cfgir/ast.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
use crate::{
66
diagnostics::WarningFilters,
7-
expansion::ast::{Attributes, Friend, ModuleIdent, Visibility},
7+
expansion::ast::{Attributes, Friend, ModuleIdent},
88
hlir::ast::{
99
BaseType, Command, Command_, FunctionSignature, Label, SingleType, StructDefinition, Var,
10+
Visibility,
1011
},
1112
parser::ast::{ConstantName, FunctionName, StructName, ENTRY_MODIFIER},
1213
shared::{ast_debug::*, unique_map::UniqueMap},

move-compiler/src/diagnostics/codes.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ codes!(
202202
InvalidNonPhantomUse:
203203
{ msg: "invalid non-phantom type parameter usage", severity: Warning },
204204
InvalidAttribute: { msg: "invalid attribute", severity: NonblockingError },
205+
InvalidVisibilityModifier:
206+
{ msg: "invalid visibility modifier", severity: NonblockingError },
205207
],
206208
// errors name resolution, mostly expansion/translate and naming/translate
207209
NameResolution: [

move-compiler/src/editions/mod.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ pub struct Edition {
2626
}
2727

2828
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
29-
pub enum FeatureGate {}
29+
pub enum FeatureGate {
30+
PublicPackage,
31+
}
3032

3133
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord, Default)]
3234
pub enum Flavor {
@@ -39,19 +41,39 @@ pub enum Flavor {
3941
// Entry
4042
//**************************************************************************************************
4143

42-
pub fn check_feature(env: &mut CompilationEnv, edition: Edition, loc: Loc, feature: FeatureGate) {
43-
let is_supported = SUPPORTED_FEATURES.get(&edition).unwrap().contains(&feature);
44-
if !is_supported {
45-
env.add_diag(diag!(
44+
pub fn check_feature(env: &mut CompilationEnv, edition: Edition, feature: &FeatureGate, loc: Loc) {
45+
if !edition.supports(feature) {
46+
let valid_editions = valid_editions_for_feature(feature)
47+
.into_iter()
48+
.map(|e| e.to_string())
49+
.collect::<Vec<_>>()
50+
.join(", ");
51+
let mut diag = diag!(
4652
Editions::FeatureTooNew,
4753
(
4854
loc,
49-
format!("{feature} requires edition {edition} or newer")
55+
format!(
56+
"{feature} not supported by current edition '{edition}', \
57+
only '{valid_editions}' support this feature"
58+
)
5059
)
51-
))
60+
);
61+
diag.add_note(
62+
"You can update the edition in the 'Move.toml', \
63+
or via command line flag if invoking the compiler directly.",
64+
);
65+
env.add_diag(diag);
5266
}
5367
}
5468

69+
pub fn valid_editions_for_feature(feature: &FeatureGate) -> Vec<Edition> {
70+
Edition::ALL
71+
.iter()
72+
.filter(|e| e.supports(feature))
73+
.copied()
74+
.collect()
75+
}
76+
5577
//**************************************************************************************************
5678
// impls
5779
//**************************************************************************************************
@@ -73,6 +95,10 @@ impl Edition {
7395

7496
pub const ALL: &[Self] = &[Self::LEGACY, Self::E2024_ALPHA];
7597

98+
pub fn supports(&self, feature: &FeatureGate) -> bool {
99+
SUPPORTED_FEATURES.get(self).unwrap().contains(feature)
100+
}
101+
76102
// Intended only for implementing the lazy static (supported feature map) above
77103
fn prev(&self) -> Option<Self> {
78104
match *self {
@@ -87,7 +113,11 @@ impl Edition {
87113
fn features(&self) -> BTreeSet<FeatureGate> {
88114
match *self {
89115
Self::LEGACY => BTreeSet::new(),
90-
Self::E2024_ALPHA => self.prev().unwrap().features(),
116+
Self::E2024_ALPHA => {
117+
let mut features = self.prev().unwrap().features();
118+
features.extend([FeatureGate::PublicPackage]);
119+
features
120+
}
91121
_ => self.unknown_edition_panic(),
92122
}
93123
}

move-compiler/src/expansion/ast.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ pub type Attributes = UniqueMap<AttributeName, Attribute>;
8888
#[derive(Debug, Clone)]
8989
pub struct Script {
9090
pub warning_filter: WarningFilters,
91-
// package name metadata from compiler arguments, not used for any language rules
91+
// package name metadata from compiler arguments.
92+
// It is used primarily for retrieving the associated `PackageConfig`,
93+
// but it is also used in determining public(package) visibility.
9294
pub package_name: Option<Symbol>,
9395
pub attributes: Attributes,
9496
pub loc: Loc,
@@ -191,6 +193,7 @@ pub enum StructFields {
191193
pub enum Visibility {
192194
Public(Loc),
193195
Friend(Loc),
196+
Package(Loc),
194197
Internal,
195198
}
196199

@@ -719,13 +722,18 @@ impl AbilitySet {
719722
}
720723

721724
impl Visibility {
722-
pub const PUBLIC: &'static str = P::Visibility::PUBLIC;
723725
pub const FRIEND: &'static str = P::Visibility::FRIEND;
726+
pub const FRIEND_IDENT: &'static str = P::Visibility::FRIEND_IDENT;
724727
pub const INTERNAL: &'static str = P::Visibility::INTERNAL;
728+
pub const PACKAGE: &'static str = P::Visibility::PACKAGE;
729+
pub const PACKAGE_IDENT: &'static str = P::Visibility::PACKAGE_IDENT;
730+
pub const PUBLIC: &'static str = P::Visibility::PUBLIC;
725731

726732
pub fn loc(&self) -> Option<Loc> {
727733
match self {
728-
Visibility::Public(loc) | Visibility::Friend(loc) => Some(*loc),
734+
Visibility::Friend(loc) | Visibility::Package(loc) | Visibility::Public(loc) => {
735+
Some(*loc)
736+
}
729737
Visibility::Internal => None,
730738
}
731739
}
@@ -837,6 +845,7 @@ impl fmt::Display for Visibility {
837845
match &self {
838846
Visibility::Public(_) => Visibility::PUBLIC,
839847
Visibility::Friend(_) => Visibility::FRIEND,
848+
Visibility::Package(_) => Visibility::PACKAGE,
840849
Visibility::Internal => Visibility::INTERNAL,
841850
}
842851
)

move-compiler/src/expansion/translate.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use crate::{
66
diag,
77
diagnostics::{codes::WarningFilter, Diagnostic, WarningFilters},
8+
editions::FeatureGate,
89
expansion::{
910
aliases::{AliasMap, AliasSet},
1011
ast::{self as E, Address, Fields, ModuleIdent, ModuleIdent_, SpecId},
@@ -470,6 +471,9 @@ fn module_(
470471
P::ModuleMember::Spec(s) => specs.push(spec(context, s)),
471472
}
472473
}
474+
475+
check_visibility_modifiers(context, &functions, &friends, package_name);
476+
473477
context.set_to_outer_scope(old_aliases);
474478

475479
let def = E::ModuleDefinition {
@@ -491,6 +495,84 @@ fn module_(
491495
(current_module, def)
492496
}
493497

498+
fn check_visibility_modifiers(
499+
context: &mut Context,
500+
functions: &UniqueMap<FunctionName, E::Function>,
501+
friends: &UniqueMap<ModuleIdent, E::Friend>,
502+
package_name: Option<Symbol>,
503+
) {
504+
let mut friend_usage = friends.iter().next().map(|(_, _, friend)| friend.loc);
505+
let mut public_package_usage = None;
506+
for (_, _, function) in functions {
507+
match function.visibility {
508+
E::Visibility::Friend(loc) if friend_usage.is_none() => {
509+
friend_usage = Some(loc);
510+
}
511+
E::Visibility::Package(loc) => {
512+
context
513+
.env
514+
.check_feature(&FeatureGate::PublicPackage, package_name, loc);
515+
public_package_usage = Some(loc);
516+
}
517+
_ => (),
518+
}
519+
}
520+
521+
// Emit any errors.
522+
if public_package_usage.is_some() && friend_usage.is_some() {
523+
let friend_error_msg = format!(
524+
"Cannot define 'friend' modules and use '{}' visibility in the same module",
525+
E::Visibility::PACKAGE
526+
);
527+
let package_definition_msg = format!("'{}' visibility used here", E::Visibility::PACKAGE);
528+
for (_, _, friend) in friends {
529+
context.env.add_diag(diag!(
530+
Declarations::InvalidVisibilityModifier,
531+
(friend.loc, friend_error_msg.clone()),
532+
(
533+
public_package_usage.unwrap(),
534+
package_definition_msg.clone()
535+
)
536+
));
537+
}
538+
let package_error_msg = format!(
539+
"Cannot mix '{}' and '{}' visibilities in the same module",
540+
E::Visibility::PACKAGE_IDENT,
541+
E::Visibility::FRIEND_IDENT
542+
);
543+
let friend_error_msg = format!(
544+
"Cannot mix '{}' and '{}' visibilities in the same module",
545+
E::Visibility::FRIEND_IDENT,
546+
E::Visibility::PACKAGE_IDENT
547+
);
548+
for (_, _, function) in functions {
549+
match function.visibility {
550+
E::Visibility::Friend(loc) => {
551+
context.env.add_diag(diag!(
552+
Declarations::InvalidVisibilityModifier,
553+
(loc, friend_error_msg.clone()),
554+
(
555+
public_package_usage.unwrap(),
556+
package_definition_msg.clone()
557+
)
558+
));
559+
}
560+
E::Visibility::Package(loc) => {
561+
context.env.add_diag(diag!(
562+
Declarations::InvalidVisibilityModifier,
563+
(loc, package_error_msg.clone()),
564+
(
565+
friend_usage.unwrap(),
566+
&format!("'{}' visibility used here", E::Visibility::FRIEND_IDENT)
567+
)
568+
));
569+
}
570+
_ => {}
571+
}
572+
}
573+
}
574+
}
575+
494576
fn script(
495577
context: &mut Context,
496578
scripts: &mut Vec<E::Script>,
@@ -536,7 +618,7 @@ fn script_(context: &mut Context, package_name: Option<Symbol>, pscript: P::Scri
536618
check_valid_module_member_name(context, ModuleMemberKind::Function, pfunction.name.0);
537619
let (function_name, function) = function_(context, 0, pfunction);
538620
match &function.visibility {
539-
E::Visibility::Public(loc) | E::Visibility::Friend(loc) => {
621+
E::Visibility::Friend(loc) | E::Visibility::Package(loc) | E::Visibility::Public(loc) => {
540622
let msg = format!(
541623
"Invalid '{}' visibility modifier. \
542624
Script functions are not callable from other Move functions.",
@@ -1367,13 +1449,14 @@ fn function_(
13671449

13681450
fn visibility(context: &mut Context, pvisibility: P::Visibility) -> E::Visibility {
13691451
match pvisibility {
1452+
P::Visibility::Friend(loc) => E::Visibility::Friend(loc),
1453+
P::Visibility::Internal => E::Visibility::Internal,
1454+
P::Visibility::Package(loc) => E::Visibility::Package(loc),
13701455
P::Visibility::Public(loc) => E::Visibility::Public(loc),
13711456
P::Visibility::Script(loc) => {
13721457
assert!(!context.env.has_errors());
13731458
E::Visibility::Public(loc)
13741459
}
1375-
P::Visibility::Friend(loc) => E::Visibility::Friend(loc),
1376-
P::Visibility::Internal => E::Visibility::Internal,
13771460
}
13781461
}
13791462

move-compiler/src/hlir/ast.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use crate::{
66
diagnostics::WarningFilters,
77
expansion::ast::{
88
ability_modifiers_ast_debug, AbilitySet, Attributes, Friend, ModuleIdent, SpecId,
9-
Visibility,
109
},
1110
naming::ast::{BuiltinTypeName, BuiltinTypeName_, StructTypeParameter, TParam},
12-
parser::ast::{BinOp, ConstantName, Field, FunctionName, StructName, UnaryOp, ENTRY_MODIFIER},
11+
parser::ast::{
12+
self as P, BinOp, ConstantName, Field, FunctionName, StructName, UnaryOp, ENTRY_MODIFIER,
13+
},
1314
shared::{ast_debug::*, unique_map::UniqueMap, Name, NumericalAddress, TName},
1415
};
1516
use move_ir_types::location::*;
@@ -103,6 +104,14 @@ pub struct Constant {
103104
// Functions
104105
//**************************************************************************************************
105106

107+
// package visibility is removed after typing is done
108+
#[derive(PartialEq, Eq, Debug, Clone)]
109+
pub enum Visibility {
110+
Public(Loc),
111+
Friend(Loc),
112+
Internal,
113+
}
114+
106115
#[derive(PartialEq, Eq, Debug, Clone)]
107116
pub struct FunctionSignature {
108117
pub type_parameters: Vec<TParam>,
@@ -395,6 +404,19 @@ impl Var {
395404
}
396405
}
397406

407+
impl Visibility {
408+
pub const FRIEND: &'static str = P::Visibility::FRIEND;
409+
pub const INTERNAL: &'static str = P::Visibility::INTERNAL;
410+
pub const PUBLIC: &'static str = P::Visibility::PUBLIC;
411+
412+
pub fn loc(&self) -> Option<Loc> {
413+
match self {
414+
Visibility::Friend(loc) | Visibility::Public(loc) => Some(*loc),
415+
Visibility::Internal => None,
416+
}
417+
}
418+
}
419+
398420
impl Command_ {
399421
pub fn is_terminal(&self) -> bool {
400422
use Command_::*;
@@ -739,6 +761,20 @@ impl std::fmt::Display for TypeName_ {
739761
}
740762
}
741763

764+
impl std::fmt::Display for Visibility {
765+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
766+
write!(
767+
f,
768+
"{}",
769+
match &self {
770+
Visibility::Public(_) => Visibility::PUBLIC,
771+
Visibility::Friend(_) => Visibility::FRIEND,
772+
Visibility::Internal => Visibility::INTERNAL,
773+
}
774+
)
775+
}
776+
}
777+
742778
impl std::fmt::Display for Label {
743779
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
744780
write!(f, "{}", self.0)
@@ -948,6 +984,12 @@ impl AstDebug for FunctionSignature {
948984
}
949985
}
950986

987+
impl AstDebug for Visibility {
988+
fn ast_debug(&self, w: &mut AstWriter) {
989+
w.write(&format!("{} ", self))
990+
}
991+
}
992+
951993
impl AstDebug for Var {
952994
fn ast_debug(&self, w: &mut AstWriter) {
953995
w.write(&format!("{}", self.0))

move-compiler/src/hlir/translate.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ fn function(context: &mut Context, _name: FunctionName, f: T::Function) -> H::Fu
293293
warning_filter,
294294
index,
295295
attributes,
296-
visibility,
296+
visibility: evisibility,
297297
entry,
298298
signature,
299299
acquires,
@@ -307,7 +307,7 @@ fn function(context: &mut Context, _name: FunctionName, f: T::Function) -> H::Fu
307307
warning_filter,
308308
index,
309309
attributes,
310-
visibility,
310+
visibility: visibility(evisibility),
311311
entry,
312312
signature,
313313
acquires,
@@ -384,6 +384,16 @@ fn function_body_defined(
384384
(locals, body)
385385
}
386386

387+
fn visibility(evisibility: E::Visibility) -> H::Visibility {
388+
match evisibility {
389+
E::Visibility::Internal => H::Visibility::Internal,
390+
E::Visibility::Friend(loc) => H::Visibility::Friend(loc),
391+
// We added any friends we needed during typing, so we convert this over.
392+
E::Visibility::Package(loc) => H::Visibility::Friend(loc),
393+
E::Visibility::Public(loc) => H::Visibility::Public(loc),
394+
}
395+
}
396+
387397
//**************************************************************************************************
388398
// Constants
389399
//**************************************************************************************************

0 commit comments

Comments
 (0)