diff --git a/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs b/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs index c25afc6e515ba..c5063257d9ff1 100644 --- a/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs +++ b/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs @@ -3,8 +3,11 @@ //! This module defines the gas parameters for Move Stdlib. -use crate::{gas_feature_versions::RELEASE_V1_18, gas_schedule::NativeGasParameters}; -use aptos_gas_algebra::{InternalGas, InternalGasPerByte}; +use crate::{ + gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_22}, + gas_schedule::NativeGasParameters, +}; +use aptos_gas_algebra::{InternalGas, InternalGasPerAbstractValueUnit, InternalGasPerByte}; crate::gas_schedule::macros::define_gas_parameters!( MoveStdlibGasParameters, @@ -36,5 +39,8 @@ crate::gas_schedule::macros::define_gas_parameters!( [bcs_serialized_size_base: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.base" }, 735], [bcs_serialized_size_per_byte_serialized: InternalGasPerByte, { RELEASE_V1_18.. => "bcs.serialized_size.per_byte_serialized" }, 36], [bcs_serialized_size_failure: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.failure" }, 3676], + + [mem_swap_base: InternalGas, { RELEASE_V1_22.. => "mem.swap.base" }, 367], + [mem_swap_per_abs_val_unit: InternalGasPerAbstractValueUnit, { RELEASE_V1_22.. => "mem.swap.per_abs_val_unit"}, 14], ] ); diff --git a/aptos-move/aptos-gas-schedule/src/ver.rs b/aptos-move/aptos-gas-schedule/src/ver.rs index f798c42b40145..d958276339a46 100644 --- a/aptos-move/aptos-gas-schedule/src/ver.rs +++ b/aptos-move/aptos-gas-schedule/src/ver.rs @@ -69,7 +69,7 @@ /// global operations. /// - V1 /// - TBA -pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_21; +pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_22; pub mod gas_feature_versions { pub const RELEASE_V1_8: u64 = 11; @@ -86,4 +86,5 @@ pub mod gas_feature_versions { pub const RELEASE_V1_19: u64 = 23; pub const RELEASE_V1_20: u64 = 24; pub const RELEASE_V1_21: u64 = 25; + pub const RELEASE_V1_22: u64 = 26; } diff --git a/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib/sources/mem.move b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib/sources/mem.move new file mode 120000 index 0000000000000..13c1b4e1d1a8f --- /dev/null +++ b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib/sources/mem.move @@ -0,0 +1 @@ +../../../../../../framework/move-stdlib/sources/mem.move \ No newline at end of file diff --git a/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib_incompat/sources/mem.move b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib_incompat/sources/mem.move new file mode 120000 index 0000000000000..13c1b4e1d1a8f --- /dev/null +++ b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib_incompat/sources/mem.move @@ -0,0 +1 @@ +../../../../../../framework/move-stdlib/sources/mem.move \ No newline at end of file diff --git a/aptos-move/framework/move-stdlib/doc/mem.md b/aptos-move/framework/move-stdlib/doc/mem.md new file mode 100644 index 0000000000000..cce483a926275 --- /dev/null +++ b/aptos-move/framework/move-stdlib/doc/mem.md @@ -0,0 +1,113 @@ + + + +# Module `0x1::mem` + +Module with methods for safe memory manipulation. +I.e. swapping/replacing non-copyable/non-droppable types. + + +- [Function `swap`](#0x1_mem_swap) +- [Function `replace`](#0x1_mem_replace) +- [Specification](#@Specification_0) + - [Function `swap`](#@Specification_0_swap) + - [Function `replace`](#@Specification_0_replace) + + +
+ + + + + +## Function `swap` + +Swap contents of two passed mutable references. + + +
public fun swap<T>(left: &mut T, right: &mut T)
+
+ + + +
+Implementation + + +
public native fun swap<T>(left: &mut T, right: &mut T);
+
+ + + +
+ + + +## Function `replace` + +Replace value reference points to with the given new value, +and return value it had before. + + +
public fun replace<T>(ref: &mut T, new: T): T
+
+ + + +
+Implementation + + +
public fun replace<T>(ref: &mut T, new: T): T {
+    swap(ref, &mut new);
+    new
+}
+
+ + + +
+ + + +## Specification + + + + +### Function `swap` + + +
public fun swap<T>(left: &mut T, right: &mut T)
+
+ + + + +
pragma opaque;
+aborts_if false;
+ensures right == old(left);
+ensures left == old(right);
+
+ + + + + +### Function `replace` + + +
public fun replace<T>(ref: &mut T, new: T): T
+
+ + + + +
pragma opaque;
+aborts_if false;
+ensures result == old(ref);
+ensures ref == new;
+
+ + +[move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/move-stdlib/doc/overview.md b/aptos-move/framework/move-stdlib/doc/overview.md index 8eb0c67f05113..649873e8ab2f5 100644 --- a/aptos-move/framework/move-stdlib/doc/overview.md +++ b/aptos-move/framework/move-stdlib/doc/overview.md @@ -20,6 +20,7 @@ For on overview of the Move language, see the [Move Book][move-book]. - [`0x1::features`](features.md#0x1_features) - [`0x1::fixed_point32`](fixed_point32.md#0x1_fixed_point32) - [`0x1::hash`](hash.md#0x1_hash) +- [`0x1::mem`](mem.md#0x1_mem) - [`0x1::option`](option.md#0x1_option) - [`0x1::signer`](signer.md#0x1_signer) - [`0x1::string`](string.md#0x1_string) diff --git a/aptos-move/framework/move-stdlib/sources/mem.move b/aptos-move/framework/move-stdlib/sources/mem.move new file mode 100644 index 0000000000000..98758c43eba38 --- /dev/null +++ b/aptos-move/framework/move-stdlib/sources/mem.move @@ -0,0 +1,27 @@ +/// Module with methods for safe memory manipulation. +/// I.e. swapping/replacing non-copyable/non-droppable types. +module std::mem { + /// Swap contents of two passed mutable references. + public native fun swap(left: &mut T, right: &mut T); + + /// Replace value reference points to with the given new value, + /// and return value it had before. + public fun replace(ref: &mut T, new: T): T { + swap(ref, &mut new); + new + } + + spec swap(left: &mut T, right: &mut T) { + pragma opaque; + aborts_if false; + ensures right == old(left); + ensures left == old(right); + } + + spec replace(ref: &mut T, new: T): T { + pragma opaque; + aborts_if false; + ensures result == old(ref); + ensures ref == new; + } +} diff --git a/aptos-move/framework/move-stdlib/src/natives/mem.rs b/aptos-move/framework/move-stdlib/src/natives/mem.rs new file mode 100644 index 0000000000000..e13dccfd20ab3 --- /dev/null +++ b/aptos-move/framework/move-stdlib/src/natives/mem.rs @@ -0,0 +1,75 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +//! Implementation of native functions for utf8 strings. + +use aptos_gas_schedule::gas_params::natives::move_stdlib::{ + MEM_SWAP_BASE, MEM_SWAP_PER_ABS_VAL_UNIT, +}; +use aptos_native_interface::{ + safely_pop_arg, RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError, + SafeNativeResult, +}; +use move_core_types::vm_status::StatusCode; +use move_vm_runtime::native_functions::NativeFunction; +use move_vm_types::{ + loaded_data::runtime_types::Type, + natives::function::PartialVMError, + values::{Reference, Value}, +}; +use smallvec::{smallvec, SmallVec}; +use std::collections::VecDeque; + +/*************************************************************************************************** + * native fun native_swap + * + * gas cost: MEM_SWAP_BASE + MEM_SWAP_PER_ABS_VAL_UNIT * abstract_size_of_arguments + * + **************************************************************************************************/ +fn native_swap( + context: &mut SafeNativeContext, + _ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + debug_assert!(args.len() == 2); + + if args.len() != 2 { + return Err(SafeNativeError::InvariantViolation(PartialVMError::new( + StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, + ))); + } + + let cost = MEM_SWAP_BASE + + MEM_SWAP_PER_ABS_VAL_UNIT + * (context.abs_val_size(&args[0]) + context.abs_val_size(&args[1])); + context.charge(cost)?; + + let ref1 = safely_pop_arg!(args, Reference); + let ref0 = safely_pop_arg!(args, Reference); + + ref0.swap_ref(|value0| { + let mut value1_opt = Option::None; + ref1.swap_ref(|value1| { + value1_opt = Option::Some(value1); + Ok(value0) + })?; + Ok(value1_opt.unwrap()) + })?; + + Ok(smallvec![]) +} + +/*************************************************************************************************** + * module + **************************************************************************************************/ +pub fn make_all( + builder: &SafeNativeBuilder, +) -> impl Iterator + '_ { + let natives = [("swap", native_swap as RawSafeNative)]; + + builder.make_named_natives(natives) +} diff --git a/aptos-move/framework/move-stdlib/src/natives/mod.rs b/aptos-move/framework/move-stdlib/src/natives/mod.rs index 56b37bd332960..24c995f7e9cec 100644 --- a/aptos-move/framework/move-stdlib/src/natives/mod.rs +++ b/aptos-move/framework/move-stdlib/src/natives/mod.rs @@ -7,6 +7,7 @@ pub mod bcs; pub mod hash; +pub mod mem; pub mod signer; pub mod string; #[cfg(feature = "testing")] @@ -33,6 +34,7 @@ pub fn all_natives( builder.with_incremental_gas_charging(false, |builder| { add_natives!("bcs", bcs::make_all(builder)); add_natives!("hash", hash::make_all(builder)); + add_natives!("mem", mem::make_all(builder)); add_natives!("signer", signer::make_all(builder)); add_natives!("string", string::make_all(builder)); #[cfg(feature = "testing")] diff --git a/aptos-move/framework/move-stdlib/tests/mem_tests.move b/aptos-move/framework/move-stdlib/tests/mem_tests.move new file mode 100644 index 0000000000000..14144ee4b3513 --- /dev/null +++ b/aptos-move/framework/move-stdlib/tests/mem_tests.move @@ -0,0 +1,65 @@ +#[test_only] +module std::mem_tests { + use std::vector; + use std::mem::{swap, replace}; + + #[test] + fun test_swap_ints() { + let a = 1; + let b = 2; + let v = vector[3, 4, 5, 6]; + + swap(&mut a, &mut b); + assert!(a == 2, 0); + assert!(b == 1, 1); + + swap(&mut a, vector::borrow_mut(&mut v, 0)); + assert!(a == 3, 0); + assert!(vector::borrow(&v, 0) == &2, 1); + + swap(vector::borrow_mut(&mut v, 2), &mut a); + assert!(a == 5, 0); + assert!(vector::borrow(&v, 2) == &3, 1); + } + + + #[test] + fun test_replace_ints() { + let a = 1; + let b = 2; + + assert!(replace(&mut a, b) == 1, 0); + assert!(a == 2, 1); + } + + #[test_only] + struct SomeStruct has drop { + f: u64, + v: vector, + } + + #[test] + fun test_swap_struct() { + let a = 1; + let s1 = SomeStruct { f: 2, v: vector[3, 4]}; + let s2 = SomeStruct { f: 5, v: vector[6, 7]}; + let vs = vector[SomeStruct { f: 8, v: vector[9, 10]}, SomeStruct { f: 11, v: vector[12, 13]}]; + + + swap(&mut s1, &mut s2); + assert!(&s1 == &SomeStruct { f: 5, v: vector[6, 7]}, 0); + assert!(&s2 == &SomeStruct { f: 2, v: vector[3, 4]}, 1); + + swap(&mut s1.f, &mut a); + assert!(s1.f == 1, 2); + assert!(a == 5, 3); + + swap(&mut s1.f, vector::borrow_mut(&mut s1.v, 0)); + assert!(s1.f == 6, 4); + assert!(vector::borrow(&s1.v, 0) == &1, 5); + + swap(&mut s2, vector::borrow_mut(&mut vs, 0)); + assert!(&s2 == &SomeStruct { f: 8, v: vector[9, 10]}, 6); + assert!(vector::borrow(&vs, 0) == &SomeStruct { f: 2, v: vector[3, 4]}, 7); + } +} diff --git a/execution/executor/tests/internal_indexer_test.rs b/execution/executor/tests/internal_indexer_test.rs index ba6b004de55c9..52713351abde7 100644 --- a/execution/executor/tests/internal_indexer_test.rs +++ b/execution/executor/tests/internal_indexer_test.rs @@ -20,7 +20,7 @@ use aptos_types::{ account_config::aptos_test_root_address, block_metadata::BlockMetadata, chain_id::ChainId, - state_store::state_key::prefix::StateKeyPrefix, + state_store::state_key::{prefix::StateKeyPrefix, StateKey}, test_helpers::transaction_test_helpers::TEST_BLOCK_EXECUTOR_ONCHAIN_CONFIG, transaction::{ signature_verified_transaction::into_signature_verified_block, @@ -28,8 +28,9 @@ use aptos_types::{ WriteSetPayload, }, }; +use move_core_types::{ident_str, language_storage::StructTag}; use rand::SeedableRng; -use std::sync::Arc; +use std::{fmt::Debug, str::FromStr, sync::Arc}; const B: u64 = 1_000_000_000; @@ -192,6 +193,208 @@ fn test_db_indexer_data() { total_version, ) .unwrap(); - let address_one_kv_res: Vec<_> = address_one_kv_iter.collect(); - assert_eq!(address_one_kv_res.len(), 152); + let address_one_kv_res = address_one_kv_iter.collect::, _>>().unwrap(); + + let (code, resources): (Vec<_>, Vec<_>) = address_one_kv_res + .into_iter() + .map(|(s, _)| s) + .partition(|s| s.is_aptos_code()); + + let expected_code = vec![ + ident_str!("acl"), + ident_str!("any"), + ident_str!("bcs"), + ident_str!("dkg"), + ident_str!("mem"), + ident_str!("code"), + ident_str!("coin"), + ident_str!("guid"), + ident_str!("hash"), + ident_str!("jwks"), + ident_str!("util"), + ident_str!("block"), + ident_str!("debug"), + ident_str!("error"), + ident_str!("event"), + ident_str!("stake"), + ident_str!("table"), + ident_str!("math64"), + ident_str!("object"), + ident_str!("option"), + ident_str!("signer"), + ident_str!("string"), + ident_str!("vector"), + ident_str!("voting"), + ident_str!("account"), + ident_str!("ed25519"), + ident_str!("genesis"), + ident_str!("math128"), + ident_str!("version"), + ident_str!("vesting"), + ident_str!("bls12381"), + ident_str!("chain_id"), + ident_str!("features"), + ident_str!("from_bcs"), + ident_str!("pool_u64"), + ident_str!("secp256k1"), + ident_str!("timestamp"), + ident_str!("type_info"), + ident_str!("aggregator"), + ident_str!("aptos_coin"), + ident_str!("aptos_hash"), + ident_str!("big_vector"), + ident_str!("bit_vector"), + ident_str!("capability"), + ident_str!("comparator"), + ident_str!("math_fixed"), + ident_str!("randomness"), + ident_str!("simple_map"), + ident_str!("smart_table"), + ident_str!("storage_gas"), + ident_str!("chain_status"), + ident_str!("copyable_any"), + ident_str!("gas_schedule"), + ident_str!("managed_coin"), + ident_str!("math_fixed64"), + ident_str!("ristretto255"), + ident_str!("smart_vector"), + ident_str!("string_utils"), + ident_str!("aggregator_v2"), + ident_str!("aptos_account"), + ident_str!("bn254_algebra"), + ident_str!("config_buffer"), + ident_str!("create_signer"), + ident_str!("fixed_point32"), + ident_str!("fixed_point64"), + ident_str!("function_info"), + ident_str!("multi_ed25519"), + ident_str!("staking_proxy"), + ident_str!("state_storage"), + ident_str!("crypto_algebra"), + ident_str!("fungible_asset"), + ident_str!("staking_config"), + ident_str!("delegation_pool"), + ident_str!("keyless_account"), + ident_str!("reconfiguration"), + ident_str!("transaction_fee"), + ident_str!("aptos_governance"), + ident_str!("bls12381_algebra"), + ident_str!("consensus_config"), + ident_str!("execution_config"), + ident_str!("multisig_account"), + ident_str!("pool_u64_unbound"), + ident_str!("resource_account"), + ident_str!("staking_contract"), + ident_str!("system_addresses"), + ident_str!("randomness_config"), + ident_str!("table_with_length"), + ident_str!("aggregator_factory"), + ident_str!("governance_proposal"), + ident_str!("optional_aggregator"), + ident_str!("transaction_context"), + ident_str!("jwk_consensus_config"), + ident_str!("ristretto255_elgamal"), + ident_str!("reconfiguration_state"), + ident_str!("ristretto255_pedersen"), + ident_str!("object_code_deployment"), + ident_str!("primary_fungible_store"), + ident_str!("transaction_validation"), + ident_str!("randomness_api_v0_config"), + ident_str!("randomness_config_seqnum"), + ident_str!("reconfiguration_with_dkg"), + ident_str!("validator_consensus_info"), + ident_str!("ristretto255_bulletproofs"), + ident_str!("dispatchable_fungible_asset"), + ] + .into_iter() + .map(|module| StateKey::module(&AccountAddress::ONE, module)) + .collect::>(); + + assert_vec_eq(&code, &expected_code); + + let expected_resources = vec![ + (false, "0x1::dkg::DKGState"), + (false, "0x1::jwks::Patches"), + (false, "0x1::account::Account"), + (false, "0x1::version::Version"), + (false, "0x1::jwks::PatchedJWKs"), + (false, "0x1::chain_id::ChainId"), + (false, "0x1::coin::SupplyConfig"), + (false, "0x1::jwks::ObservedJWKs"), + (false, "0x1::features::Features"), + (false, "0x1::stake::ValidatorSet"), + (false, "0x1::block::BlockResource"), + (false, "0x1::block::CommitHistory"), + (false, "0x1::code::PackageRegistry"), + (true, "0x1::keyless_account::Group"), + (false, "0x1::coin::CoinConversionMap"), + (false, "0x1::storage_gas::StorageGas"), + (false, "0x1::stake::ValidatorPerformance"), + (false, "0x1::account::OriginatingAddress"), + (false, "0x1::gas_schedule::GasScheduleV2"), + (false, "0x1::jwks::SupportedOIDCProviders"), + (false, "0x1::stake::AptosCoinCapabilities"), + (false, "0x1::reconfiguration_state::State"), + (false, "0x1::version::SetVersionCapability"), + (false, "0x1::storage_gas::StorageGasConfig"), + (false, "0x1::config_buffer::PendingConfigs"), + (false, "0x1::staking_config::StakingConfig"), + (false, "0x1::randomness::PerBlockRandomness"), + (false, "0x1::chain_status::GenesisEndMarker"), + (false, "0x1::reconfiguration::Configuration"), + (false, "0x1::aptos_governance::VotingRecords"), + (false, "0x1::state_storage::StateStorageUsage"), + (false, "0x1::consensus_config::ConsensusConfig"), + (false, "0x1::execution_config::ExecutionConfig"), + (false, "0x1::timestamp::CurrentTimeMicroseconds"), + (false, "0x1::aptos_governance::GovernanceConfig"), + (false, "0x1::aptos_governance::GovernanceEvents"), + (false, "0x1::randomness_config::RandomnessConfig"), + (false, "0x1::aggregator_factory::AggregatorFactory"), + (false, "0x1::transaction_fee::AptosCoinCapabilities"), + (false, "0x1::transaction_fee::AptosCoinMintCapability"), + (false, "0x1::jwk_consensus_config::JWKConsensusConfig"), + (false, "0x1::aptos_governance::ApprovedExecutionHashes"), + (false, "0x1::aptos_governance::GovernanceResponsbility"), + (false, "0x1::randomness_api_v0_config::RequiredGasDeposit"), + (false, "0x1::transaction_validation::TransactionValidation"), + ( + false, + "0x1::randomness_api_v0_config::AllowCustomMaxGasFlag", + ), + ( + false, + "0x1::randomness_config_seqnum::RandomnessConfigSeqNum", + ), + (false, "0x1::coin::CoinInfo<0x1::aptos_coin::AptosCoin>"), + ( + false, + "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", + ), + ] + .into_iter() + .map(|(rg, struct_tag)| { + if rg { + StateKey::resource_group( + &AccountAddress::ONE, + &StructTag::from_str(struct_tag).unwrap(), + ) + } else { + StateKey::resource( + &AccountAddress::ONE, + &StructTag::from_str(struct_tag).unwrap(), + ) + .unwrap() + } + }) + .collect::>(); + + assert_vec_eq(&resources, &expected_resources); +} + +fn assert_vec_eq(left: &[T], right: &[T]) { + for i in 0..left.len().min(right.len()) { + assert_eq!(left[i], right[i], "difference at position {}", i); + } + assert_eq!(left.len(), right.len(), "difference at last element"); } diff --git a/third_party/move/move-vm/types/src/values/values_impl.rs b/third_party/move/move-vm/types/src/values/values_impl.rs index 006c16d6b9dd1..9db5b3a678ef7 100644 --- a/third_party/move/move-vm/types/src/values/values_impl.rs +++ b/third_party/move/move-vm/types/src/values/values_impl.rs @@ -903,6 +903,81 @@ impl Reference { } } +/************************************************************************************** + * + * Swap reference (Move) + * + * Implementation of the Move operation to swap contents of a reference. + * + *************************************************************************************/ + +impl ContainerRef { + pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()> + where + F: FnOnce(Value) -> PartialVMResult, + { + // same as read_ref, but without consuming self. + // But safe because even though we copy here, and temporarily leave a duplicate inside, + // we replace it in write_ref below. + let old_value = Value(ValueImpl::Container(self.container().copy_value()?)); + self.write_ref(swap_with(old_value)?)?; + + Ok(()) + } +} + +impl IndexedRef { + pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()> + where + F: FnOnce(Value) -> PartialVMResult, + { + use Container::*; + + // same as read_ref, but without consuming self. + // But safe because even though we copy here, and temporarily leave a duplicate inside, + // we replace it in write_ref below. + let old_value = Value(match self.container_ref.container() { + Vec(r) => r.borrow()[self.idx].copy_value()?, + Struct(r) => r.borrow()[self.idx].copy_value()?, + + VecU8(r) => ValueImpl::U8(r.borrow()[self.idx]), + VecU16(r) => ValueImpl::U16(r.borrow()[self.idx]), + VecU32(r) => ValueImpl::U32(r.borrow()[self.idx]), + VecU64(r) => ValueImpl::U64(r.borrow()[self.idx]), + VecU128(r) => ValueImpl::U128(r.borrow()[self.idx]), + VecU256(r) => ValueImpl::U256(r.borrow()[self.idx]), + VecBool(r) => ValueImpl::Bool(r.borrow()[self.idx]), + VecAddress(r) => ValueImpl::Address(r.borrow()[self.idx]), + + Locals(r) => r.borrow()[self.idx].copy_value()?, + }); + + self.write_ref(swap_with(old_value)?)?; + Ok(()) + } +} + +impl ReferenceImpl { + pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()> + where + F: FnOnce(Value) -> PartialVMResult, + { + match self { + Self::ContainerRef(r) => r.swap_ref(swap_with), + Self::IndexedRef(r) => r.swap_ref(swap_with), + } + } +} + +impl Reference { + pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()> + where + F: FnOnce(Value) -> PartialVMResult, + { + self.0.swap_ref(swap_with) + } +} + /*************************************************************************************** * * Borrows (Move)