From 930ffacda192e7be0224ef33f41441ebeda4a6d2 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Fri, 23 Feb 2024 11:30:54 -0800 Subject: [PATCH] [move 2024] Added implicit/default aliases. Fixed edgecase with builtin names (#16339) ## Description - Fixed an edge case with builtin names, where `` would resolve to a module after a `use a::` - 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}; ``` --- .../move-compiler/src/expansion/translate.rs | 133 +++++++++++++++++- .../always_module_member_without_chain.move | 13 ++ .../expansion/implicit_std_aliases.move | 11 ++ .../expansion/leading_builtin_name.move | 15 ++ .../expansion/implicit_sui_aliases.move | 35 +++++ 5 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/always_module_member_without_chain.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/implicit_std_aliases.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/expansion/leading_builtin_name.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/expansion/implicit_sui_aliases.move diff --git a/external-crates/move/crates/move-compiler/src/expansion/translate.rs b/external-crates/move/crates/move-compiler/src/expansion/translate.rs index 67eed4bfef26d..0f00fe4bb64ff 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/translate.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/translate.rs @@ -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, @@ -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 //************************************************************************************************** @@ -403,6 +495,8 @@ fn definition( package_name: Option, 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); @@ -428,6 +522,7 @@ fn definition( } } } + context.pop_alias_scope(None); } // Access a top level address as declared, not affected by any aliasing/shadowing @@ -482,6 +577,17 @@ fn top_level_address_( } } +fn maybe_make_well_known_address(context: &mut Context, loc: Loc, name: Symbol) -> Option
{ + 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 { @@ -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>, + address: Option>, ) { - context.address = Some(match sender { + context.address = Some(match address { Some(sp!(_, addr)) => addr, None => { let loc = module_name.loc(); @@ -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!( @@ -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 @@ -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) => { diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/always_module_member_without_chain.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/always_module_member_without_chain.move new file mode 100644 index 0000000000000..45ec6a264d1cd --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/always_module_member_without_chain.move @@ -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 } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/implicit_std_aliases.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/implicit_std_aliases.move new file mode 100644 index 0000000000000..8525971e825e8 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/implicit_std_aliases.move @@ -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 } + fun wow(): vector> { + let mut v = vector::empty(); + vector::push_back(&mut v, option::none()); + v + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/expansion/leading_builtin_name.move b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/leading_builtin_name.move new file mode 100644 index 0000000000000..fd4d56bbfd499 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/expansion/leading_builtin_name.move @@ -0,0 +1,15 @@ +module a::t1 { + use std::vector; + + public entry fun ascii_vec_arg(v: vector) { + assert!(vector::is_empty(&v), 0); + } +} + +module a::t2 { + // implicit use std::vector; + + public entry fun ascii_vec_arg(v: vector) { + assert!(vector::is_empty(&v), 0); + } +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/expansion/implicit_sui_aliases.move b/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/expansion/implicit_sui_aliases.move new file mode 100644 index 0000000000000..93c61989e6f04 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/expansion/implicit_sui_aliases.move @@ -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, _: address) { abort 0 } +} +module sui::tx_context { + public struct TxContext has drop {} + public fun sender(_: &TxContext): address { @0 } +}