Skip to content

Commit

Permalink
[move 2024] Added implicit/default aliases. Fixed edgecase with built…
Browse files Browse the repository at this point in the history
…in names (#16339)

## Description 

- Fixed an edge case with builtin names, where `<builtin>` would resolve
to a module after a `use a::<builtin>`
- Added implicit aliases for `std` and `sui` 

## Test Plan 

New tests

---
If your changes are not user-facing and do not break anything, you can
skip the following section. Otherwise, please briefly describe what has
changed under the Release Notes section.

### 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

Move 2024 now has the following implicit aliases 
```
use std::vector;
use std::option::{Self, Option};
use sui::object::{Self, ID, UID};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
```
  • Loading branch information
tnowacki authored Feb 23, 2024
1 parent 2348e3f commit 930ffac
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 7 deletions.
133 changes: 126 additions & 7 deletions external-crates/move/crates/move-compiler/src/expansion/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use crate::{
diag,
diagnostics::{codes::WarningFilter, Diagnostic, WarningFilters},
editions::{create_feature_error, FeatureGate},
editions::{create_feature_error, FeatureGate, Flavor},
expansion::{
alias_map_builder::{
AliasEntry, AliasMapBuilder, NameSpace, ParserExplicitUseFun, UseFunsBuilder,
Expand Down Expand Up @@ -251,6 +251,98 @@ fn compute_address_conflicts(
.collect()
}

// Implicit aliases for the Move Stdlib:
// use std::vector;
// use std::option::{Self, Option};
const IMPLICIT_STD_MODULES: &[Symbol] = &[symbol!("option"), symbol!("vector")];
const IMPLICIT_STD_MEMBERS: &[(Symbol, Symbol, ModuleMemberKind)] = &[(
symbol!("option"),
symbol!("Option"),
ModuleMemberKind::Struct,
)];

// Implicit aliases for Sui mode:
// use sui::object::{Self, ID, UID};
// use sui::transfer;
// use sui::tx_context::{Self, TxContext};
const IMPLICIT_SUI_MODULES: &[Symbol] = &[
symbol!("object"),
symbol!("transfer"),
symbol!("tx_context"),
];
const IMPLICIT_SUI_MEMBERS: &[(Symbol, Symbol, ModuleMemberKind)] = &[
(symbol!("object"), symbol!("ID"), ModuleMemberKind::Struct),
(symbol!("object"), symbol!("UID"), ModuleMemberKind::Struct),
(
symbol!("tx_context"),
symbol!("TxContext"),
ModuleMemberKind::Struct,
),
];

fn default_aliases(context: &mut Context) -> AliasMapBuilder {
let current_package = context.current_package;
let mut builder = context.new_alias_map_builder();
if !context
.env()
.supports_feature(current_package, FeatureGate::Move2024Paths)
{
return builder;
}
// Unused loc since these will not conflict and are implicit so no warnings are given
let loc = Loc::invalid();
let std_address = maybe_make_well_known_address(context, loc, symbol!("std"));
let sui_address = maybe_make_well_known_address(context, loc, symbol!("sui"));
let mut modules: Vec<(Address, Symbol)> = vec![];
let mut members: Vec<(Address, Symbol, Symbol, ModuleMemberKind)> = vec![];
// if std is defined, add implicit std aliases
if let Some(std_address) = std_address {
modules.extend(
IMPLICIT_STD_MODULES
.iter()
.copied()
.map(|m| (std_address, m)),
);
members.extend(
IMPLICIT_STD_MEMBERS
.iter()
.copied()
.map(|(m, mem, k)| (std_address, m, mem, k)),
);
}
// if sui is defined and the current package is in Sui mode, add implicit sui aliases
if sui_address.is_some() && context.env().package_config(current_package).flavor == Flavor::Sui
{
let sui_address = sui_address.unwrap();
modules.extend(
IMPLICIT_SUI_MODULES
.iter()
.copied()
.map(|m| (sui_address, m)),
);
members.extend(
IMPLICIT_SUI_MEMBERS
.iter()
.copied()
.map(|(m, mem, k)| (sui_address, m, mem, k)),
);
}
for (addr, module) in modules {
let alias = sp(loc, module);
let mident = sp(loc, ModuleIdent_::new(addr, ModuleName(sp(loc, module))));
builder.add_implicit_module_alias(alias, mident).unwrap();
}
for (addr, module, member, kind) in members {
let alias = sp(loc, member);
let mident = sp(loc, ModuleIdent_::new(addr, ModuleName(sp(loc, module))));
let name = sp(loc, member);
builder
.add_implicit_member_alias(alias, mident, name, kind)
.unwrap();
}
builder
}

//**************************************************************************************************
// Entry
//**************************************************************************************************
Expand Down Expand Up @@ -403,6 +495,8 @@ fn definition(
package_name: Option<Symbol>,
def: P::Definition,
) {
let default_aliases = default_aliases(context);
context.push_alias_scope(/* unused */ Loc::invalid(), default_aliases);
match def {
P::Definition::Module(mut m) => {
let module_paddr = std::mem::take(&mut m.address);
Expand All @@ -428,6 +522,7 @@ fn definition(
}
}
}
context.pop_alias_scope(None);
}

// Access a top level address as declared, not affected by any aliasing/shadowing
Expand Down Expand Up @@ -482,6 +577,17 @@ fn top_level_address_(
}
}

fn maybe_make_well_known_address(context: &mut Context, loc: Loc, name: Symbol) -> Option<Address> {
let named_address_mapping = context.defn_context.named_address_mapping.as_ref().unwrap();
let addr = named_address_mapping.get(&name).copied()?;
Some(make_address(
&mut context.defn_context,
sp(loc, name),
loc,
addr,
))
}

fn address_without_value_error(suggest_declaration: bool, loc: Loc, n: &Name) -> Diagnostic {
let mut msg = format!("address '{}' is not assigned a value", n);
if suggest_declaration {
Expand Down Expand Up @@ -581,12 +687,12 @@ fn module(
context.address = None
}

fn set_sender_address(
fn set_module_address(
context: &mut Context,
module_name: &ModuleName,
sender: Option<Spanned<Address>>,
address: Option<Spanned<Address>>,
) {
context.address = Some(match sender {
context.address = Some(match address {
Some(sp!(_, addr)) => addr,
None => {
let loc = module_name.loc();
Expand Down Expand Up @@ -628,7 +734,7 @@ fn module_(
.add_warning_filter_scope(warning_filter.clone());
assert!(context.address.is_none());
assert!(address.is_none());
set_sender_address(context, &name, module_address);
set_module_address(context, &name, module_address);
let _ = check_restricted_name_all_cases(&mut context.defn_context, NameCase::Module, &name.0);
if name.value().starts_with(|c| c == '_') {
let msg = format!(
Expand Down Expand Up @@ -1425,8 +1531,8 @@ impl Move2024PathExpander {
) -> AccessChainResult {
use AccessChainFailure::*;
use AccessChainResult::*;

use E::ModuleAccess_ as EN;

match self.aliases.resolve(namespace, &name) {
Some(AliasEntry::Member(_, mident, sp!(_, mem))) => {
// We are preserving the name's original location, rather than referring to where
Expand Down Expand Up @@ -1502,15 +1608,28 @@ impl Move2024PathExpander {
use AccessChainResult::*;
use E::ModuleAccess_ as EN;
use P::NameAccessChain_ as PN;

match chain {
PN::One(name) => {
use crate::naming::ast::BuiltinFunction_;
use crate::naming::ast::BuiltinTypeName_;
let namespace = match access {
Access::Type | Access::ApplyNamed | Access::ApplyPositional | Access::Term => {
NameSpace::ModuleMembers
}
Access::Module => NameSpace::LeadingAccess,
};
self.resolve_name(context, namespace, name)

// This is a hack to let `use std::vector` play nicely with `vector`,
// plus preserve things like `u64`, etc.
if !matches!(access, Access::Module)
&& (BuiltinFunction_::all_names().contains(&name.value)
|| BuiltinTypeName_::all_names().contains(&name.value))
{
AccessChainResult::UnresolvedName(name.loc, name)
} else {
self.resolve_name(context, namespace, name)
}
}
PN::Two(root_name, name) => match self.resolve_root(context, root_name) {
Address(_, address) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// without a chain, we always assume a module member
module a::S {
public struct S()
// does not resolve to the module
fun id(s: S): S { s }
}
// extra care given for builtins
#[allow(unused_use)]
module a::u64 {
use a::u64; // unused
const C: u64 = 0;
fun new(): u64 { 0 }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// if std is defined, the implicit aliases of
// use std::vector;
// use std::option::{Self, Option};
module a::m {
public struct S { f: Option<u64> }
fun wow(): vector<Option<u64>> {
let mut v = vector::empty();
vector::push_back(&mut v, option::none());
v
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module a::t1 {
use std::vector;

public entry fun ascii_vec_arg(v: vector<u8>) {
assert!(vector::is_empty(&v), 0);
}
}

module a::t2 {
// implicit use std::vector;

public entry fun ascii_vec_arg(v: vector<u8>) {
assert!(vector::is_empty(&v), 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// sui mode has the implicit asliases:
// use sui::object::{Self, ID, UID};
// use sui::transfer;
// use sui::tx_context::{Self, TxContext};
module a::m {
public struct S has key { id: UID, other: ID }
public fun create(ctx: &mut TxContext) {
transfer::transfer(
S { id: object::new(ctx), other: object::id_from_address(@0) },
tx_context::sender(ctx),
)
}
}


// we don't link out to the sui framework
module sui::object {
public struct ID has copy, drop, store {
bytes: address
}

public struct UID has store {
id: ID,
}

public fun new(_: &mut TxContext): UID { abort 0 }
public fun id_from_address(_: address): ID { abort 0 }
}
module sui::transfer {
public fun transfer<T: key>(_: T, _: address) { abort 0 }
}
module sui::tx_context {
public struct TxContext has drop {}
public fun sender(_: &TxContext): address { @0 }
}

0 comments on commit 930ffac

Please sign in to comment.