From 7e2a370219609795bd76f8ca636fa75bfef9d90a Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Mon, 5 Dec 2022 13:54:19 +0100 Subject: [PATCH 01/16] Add account(zero_copy(safe_bytemuck_derives)) feature. The default account(zero_copy) feature unsafe impls the bytemuck traits The new one derives it instead, which runs desired sanity checks like "struct has no padding". --- lang/attribute/account/src/lib.rs | 34 +++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 273be17322..4b3ea0d600 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -66,6 +66,7 @@ pub fn account( ) -> proc_macro::TokenStream { let mut namespace = "".to_string(); let mut is_zero_copy = false; + let mut safe_bytemuck = false; let args_str = args.to_string(); let args: Vec<&str> = args_str.split(',').collect(); if args.len() > 2 { @@ -80,6 +81,10 @@ pub fn account( .collect(); if ns == "zero_copy" { is_zero_copy = true; + safe_bytemuck = false; + } else if ns == "zero_copy(safe_bytemuck_derives)" { + is_zero_copy = true; + safe_bytemuck = true; } else { namespace = ns; } @@ -123,16 +128,37 @@ pub fn account( } }; + let unsafe_bytemuck_impl = { + if !safe_bytemuck { + quote! { + #[automatically_derived] + unsafe impl #impl_gen anchor_lang::__private::bytemuck::Pod for #account_name #type_gen #where_clause {} + #[automatically_derived] + unsafe impl #impl_gen anchor_lang::__private::bytemuck::Zeroable for #account_name #type_gen #where_clause {} + } + } else { + quote! {} + } + }; + + let safe_bytemuck_derives = { + if safe_bytemuck { + quote! { + #[derive(bytemuck::Pod, bytemuck::Zeroable)] + } + } else { + quote! {} + } + }; + proc_macro::TokenStream::from({ if is_zero_copy { quote! { #[zero_copy] + #safe_bytemuck_derives #account_strct - #[automatically_derived] - unsafe impl #impl_gen anchor_lang::__private::bytemuck::Pod for #account_name #type_gen #where_clause {} - #[automatically_derived] - unsafe impl #impl_gen anchor_lang::__private::bytemuck::Zeroable for #account_name #type_gen #where_clause {} + #unsafe_bytemuck_impl #[automatically_derived] impl #impl_gen anchor_lang::ZeroCopy for #account_name #type_gen #where_clause {} From d1fe74ed801f886854b3a97b1d72df03a9353f81 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Mon, 26 Dec 2022 16:32:51 +0100 Subject: [PATCH 02/16] Make the more restrictive behavior the default, add changelog --- CHANGELOG.md | 6 ++++++ lang/attribute/account/src/lib.rs | 12 ++++++------ lang/tests/generics_test.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c03d06286..04d5c61efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,12 @@ The minor version will be incremented upon a breaking change and the patch versi ### Breaking - lang: Remove `state` and `interface` attributes ([#2285](https://github.com/coral-xyz/anchor/pull/2285)). +- lang: `account(zero_copy)` now derives the `bytemuck::Pod` and `Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). + + This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. + See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. + + Legacy applications can use `account(zero_copy(unsafe_bytemuck_impls))` for the old behavior. ## [0.26.0] - 2022-12-15 diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 4b3ea0d600..2a624318f5 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -66,7 +66,7 @@ pub fn account( ) -> proc_macro::TokenStream { let mut namespace = "".to_string(); let mut is_zero_copy = false; - let mut safe_bytemuck = false; + let mut unsafe_bytemuck = false; let args_str = args.to_string(); let args: Vec<&str> = args_str.split(',').collect(); if args.len() > 2 { @@ -81,10 +81,10 @@ pub fn account( .collect(); if ns == "zero_copy" { is_zero_copy = true; - safe_bytemuck = false; - } else if ns == "zero_copy(safe_bytemuck_derives)" { + unsafe_bytemuck = false; + } else if ns == "zero_copy(unsafe_bytemuck_impls)" { is_zero_copy = true; - safe_bytemuck = true; + unsafe_bytemuck = true; } else { namespace = ns; } @@ -129,7 +129,7 @@ pub fn account( }; let unsafe_bytemuck_impl = { - if !safe_bytemuck { + if unsafe_bytemuck { quote! { #[automatically_derived] unsafe impl #impl_gen anchor_lang::__private::bytemuck::Pod for #account_name #type_gen #where_clause {} @@ -142,7 +142,7 @@ pub fn account( }; let safe_bytemuck_derives = { - if safe_bytemuck { + if !unsafe_bytemuck { quote! { #[derive(bytemuck::Pod, bytemuck::Zeroable)] } diff --git a/lang/tests/generics_test.rs b/lang/tests/generics_test.rs index 3ecb8f3257..0b78ac88af 100644 --- a/lang/tests/generics_test.rs +++ b/lang/tests/generics_test.rs @@ -22,7 +22,7 @@ where pub associated: Account<'info, Associated>, } -#[account(zero_copy)] +#[account(zero_copy(unsafe_bytemuck_impls))] pub struct FooAccount { pub data: WrappedU8Array, } From 5df49bf667b5c1e1922f28d5a0f336e0a1f14296 Mon Sep 17 00:00:00 2001 From: henrye Date: Tue, 17 Jan 2023 11:58:41 +0000 Subject: [PATCH 03/16] update syntax usage --- lang/Cargo.toml | 2 +- lang/attribute/account/src/lib.rs | 2 +- tests/zero-copy/programs/zero-copy/Cargo.toml | 2 +- tests/zero-copy/programs/zero-copy/src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lang/Cargo.toml b/lang/Cargo.toml index 5901be7f0c..bb4328c7ad 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -35,7 +35,7 @@ anchor-derive-accounts = { path = "./derive/accounts", version = "0.26.0" } arrayref = "0.3.6" base64 = "0.13.0" borsh = "0.9" -bytemuck = "1.4.0" +bytemuck = { version = "1.4.0", features = ["derive"]} solana-program = "1.13.5" thiserror = "1.0.20" bincode = "1.3.3" diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 2a624318f5..22f4eeb4e4 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -144,7 +144,7 @@ pub fn account( let safe_bytemuck_derives = { if !unsafe_bytemuck { quote! { - #[derive(bytemuck::Pod, bytemuck::Zeroable)] + #[derive(anchor_lang::__private::bytemuck::Pod, anchor_lang::__private::bytemuck::Zeroable)] } } else { quote! {} diff --git a/tests/zero-copy/programs/zero-copy/Cargo.toml b/tests/zero-copy/programs/zero-copy/Cargo.toml index 9a608d4aa2..422ff1fcd1 100644 --- a/tests/zero-copy/programs/zero-copy/Cargo.toml +++ b/tests/zero-copy/programs/zero-copy/Cargo.toml @@ -18,8 +18,8 @@ test-bpf = [] [dependencies] anchor-lang = { path = "../../../../lang" } +bytemuck = "1.4.0" [dev-dependencies] anchor-client = { path = "../../../../client", features = ["debug"] } -bytemuck = "1.4.0" solana-program-test = "1.13.5" diff --git a/tests/zero-copy/programs/zero-copy/src/lib.rs b/tests/zero-copy/programs/zero-copy/src/lib.rs index c6076ea9ff..affc5db040 100644 --- a/tests/zero-copy/programs/zero-copy/src/lib.rs +++ b/tests/zero-copy/programs/zero-copy/src/lib.rs @@ -133,7 +133,7 @@ pub struct UpdateLargeAccount<'info> { } #[account(zero_copy)] -#[repr(packed)] +#[repr(C)] #[derive(Default)] pub struct Foo { pub authority: Pubkey, From cb4a11fcf177039d95ddc478bf4891e76006ba0f Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 15:01:54 +0000 Subject: [PATCH 04/16] use const generics with bytemuck, add derive(pod,zeroable) above other zero copy types --- lang/attribute/account/src/lib.rs | 25 +++++++++++++++++++ tests/zero-copy/programs/zero-copy/Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 22f4eeb4e4..64d5783ee0 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -328,9 +328,34 @@ pub fn zero_copy( None => quote! {#[repr(C)]}, }; + let mut has_pod_attr = false; + let mut has_zeroable_attr = false; + for attr in account_strct.attrs.iter() { + let token_string = attr.tokens.to_string(); + if token_string.contains("bytemuck :: Pod") { + has_pod_attr = true; + } + if token_string.contains("bytemuck :: Zeroable") { + has_zeroable_attr = true; + } + } + + let pod = if has_pod_attr { + quote! {} + } else { + quote! {#[derive(anchor_lang::__private::bytemuck::Pod)]} + }; + let zeroable = if has_zeroable_attr { + quote! {} + } else { + quote! {#[derive(anchor_lang::__private::bytemuck::Zeroable)]} + }; + proc_macro::TokenStream::from(quote! { #[derive(anchor_lang::__private::ZeroCopyAccessor, Copy, Clone)] #repr + #pod + #zeroable #account_strct }) } diff --git a/tests/zero-copy/programs/zero-copy/Cargo.toml b/tests/zero-copy/programs/zero-copy/Cargo.toml index 422ff1fcd1..8ca06ebf36 100644 --- a/tests/zero-copy/programs/zero-copy/Cargo.toml +++ b/tests/zero-copy/programs/zero-copy/Cargo.toml @@ -18,7 +18,7 @@ test-bpf = [] [dependencies] anchor-lang = { path = "../../../../lang" } -bytemuck = "1.4.0" +bytemuck = {version = "1.4.0", features = ["min_const_generics"]} [dev-dependencies] anchor-client = { path = "../../../../client", features = ["debug"] } From c455d7ff078e463836fa1969d62358077c6b731c Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 16:24:22 +0000 Subject: [PATCH 05/16] add (unsafe) option to zero_copy attribute --- CHANGELOG.md | 2 +- lang/attribute/account/src/lib.rs | 36 ++++++++++++++++++++++++------- lang/tests/generics_test.rs | 2 +- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04d5c61efe..c345599309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ The minor version will be incremented upon a breaking change and the patch versi This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. - Legacy applications can use `account(zero_copy(unsafe_bytemuck_impls))` for the old behavior. + Legacy applications can use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. ## [0.26.0] - 2022-12-15 diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 64d5783ee0..858d8a702d 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -82,7 +82,7 @@ pub fn account( if ns == "zero_copy" { is_zero_copy = true; unsafe_bytemuck = false; - } else if ns == "zero_copy(unsafe_bytemuck_impls)" { + } else if ns == "zero_copy(unsafe)" { is_zero_copy = true; unsafe_bytemuck = true; } else { @@ -141,21 +141,23 @@ pub fn account( } }; - let safe_bytemuck_derives = { + let bytemuck_derives = { if !unsafe_bytemuck { quote! { + #[zero_copy] #[derive(anchor_lang::__private::bytemuck::Pod, anchor_lang::__private::bytemuck::Zeroable)] } } else { - quote! {} + quote! { + #[zero_copy(unsafe)] + } } }; proc_macro::TokenStream::from({ if is_zero_copy { quote! { - #[zero_copy] - #safe_bytemuck_derives + #bytemuck_derives #account_strct #unsafe_bytemuck_impl @@ -311,9 +313,27 @@ pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::T /// ``` #[proc_macro_attribute] pub fn zero_copy( - _args: proc_macro::TokenStream, + args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { + let mut is_unsafe = false; + eprint!("args {:#?}", args); + for arg in args.into_iter() { + match arg { + proc_macro::TokenTree::Ident(ident) => { + if ident.to_string() == "unsafe" { + is_unsafe = true; + } else { + // TODO: how to return a compile error with a span (can't return prase error because expected type TokenStream) + panic!("expected single ident `unsafe`"); + } + } + _ => { + panic!("expected single ident `unsafe`"); + } + } + } + let account_strct = parse_macro_input!(item as syn::ItemStruct); // Takes the first repr. It's assumed that more than one are not on the @@ -340,12 +360,12 @@ pub fn zero_copy( } } - let pod = if has_pod_attr { + let pod = if has_pod_attr || is_unsafe { quote! {} } else { quote! {#[derive(anchor_lang::__private::bytemuck::Pod)]} }; - let zeroable = if has_zeroable_attr { + let zeroable = if has_zeroable_attr || is_unsafe { quote! {} } else { quote! {#[derive(anchor_lang::__private::bytemuck::Zeroable)]} diff --git a/lang/tests/generics_test.rs b/lang/tests/generics_test.rs index 0b78ac88af..af7daa4408 100644 --- a/lang/tests/generics_test.rs +++ b/lang/tests/generics_test.rs @@ -22,7 +22,7 @@ where pub associated: Account<'info, Associated>, } -#[account(zero_copy(unsafe_bytemuck_impls))] +#[account(zero_copy))] pub struct FooAccount { pub data: WrappedU8Array, } From b35e726c900f0d1727e14e2953e46351b9fb6b0a Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 16:40:48 +0000 Subject: [PATCH 06/16] maybe fix test --- lang/tests/generics_test.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lang/tests/generics_test.rs b/lang/tests/generics_test.rs index af7daa4408..1a506ab927 100644 --- a/lang/tests/generics_test.rs +++ b/lang/tests/generics_test.rs @@ -22,7 +22,7 @@ where pub associated: Account<'info, Associated>, } -#[account(zero_copy))] +#[account(zero_copy(unsafe))] pub struct FooAccount { pub data: WrappedU8Array, } @@ -36,7 +36,8 @@ where pub data: T, } -#[derive(Copy, Clone)] +// #[derive(Copy, Clone)] +#[zero_copy(unsafe)] pub struct WrappedU8Array(u8); impl BorshSerialize for WrappedU8Array { fn serialize(&self, _writer: &mut W) -> borsh::maybestd::io::Result<()> { From 5d0da402e56f154af1c7a73e9d0b323add77f333 Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 17:13:08 +0000 Subject: [PATCH 07/16] update cargo.toml --- tests/chat/programs/chat/Cargo.toml | 1 + tests/misc/programs/misc-optional/Cargo.toml | 1 + tests/misc/programs/misc/Cargo.toml | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/chat/programs/chat/Cargo.toml b/tests/chat/programs/chat/Cargo.toml index 45b1f10d95..b7da46c292 100644 --- a/tests/chat/programs/chat/Cargo.toml +++ b/tests/chat/programs/chat/Cargo.toml @@ -17,3 +17,4 @@ default = [] [dependencies] anchor-lang = { path = "../../../../lang" } +bytemuck = {version = "1.4.0", features = ["min_const_generics"]} diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml index e976f24902..af9c2e20de 100644 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -19,3 +19,4 @@ default = [] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } spl-associated-token-account = "1.1.1" +bytemuck = {version = "1.4.0", features = ["min_const_generics"]} diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml index f326a182f7..faf03ecbb5 100644 --- a/tests/misc/programs/misc/Cargo.toml +++ b/tests/misc/programs/misc/Cargo.toml @@ -19,3 +19,4 @@ default = [] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } spl-associated-token-account = "1.1.1" +bytemuck = {version = "1.4.0", features = ["min_const_generics"]} From b8ff1c33b12405ae35d0520557ca6d854805528c Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 17:17:12 +0000 Subject: [PATCH 08/16] fix tests --- lang/attribute/account/src/lib.rs | 1 - lang/tests/generics_test.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 858d8a702d..d0552ca60f 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -317,7 +317,6 @@ pub fn zero_copy( item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let mut is_unsafe = false; - eprint!("args {:#?}", args); for arg in args.into_iter() { match arg { proc_macro::TokenTree::Ident(ident) => { diff --git a/lang/tests/generics_test.rs b/lang/tests/generics_test.rs index 1a506ab927..a2c8f270ae 100644 --- a/lang/tests/generics_test.rs +++ b/lang/tests/generics_test.rs @@ -36,8 +36,7 @@ where pub data: T, } -// #[derive(Copy, Clone)] -#[zero_copy(unsafe)] +#[derive(Copy, Clone)] pub struct WrappedU8Array(u8); impl BorshSerialize for WrappedU8Array { fn serialize(&self, _writer: &mut W) -> borsh::maybestd::io::Result<()> { From 3d69a9306648f141daf2191e0d0bd75cfd541809 Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 17:39:41 +0000 Subject: [PATCH 09/16] update changelog --- CHANGELOG.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c345599309..f5fa2076d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,12 +21,7 @@ The minor version will be incremented upon a breaking change and the patch versi ### Breaking - lang: Remove `state` and `interface` attributes ([#2285](https://github.com/coral-xyz/anchor/pull/2285)). -- lang: `account(zero_copy)` now derives the `bytemuck::Pod` and `Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). - - This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. - See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. - - Legacy applications can use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. +- lang: `account(zero_copy)` and `zero_copy` now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. Requires adding `bytemuck = { version = "1.4.0", features = ["min_const_generics"]}` to your `cargo.toml`. Legacy applications can use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. ## [0.26.0] - 2022-12-15 From daa6701e6c1b2aab6c3bbc2c57458244bce2875e Mon Sep 17 00:00:00 2001 From: henrye Date: Wed, 18 Jan 2023 17:44:50 +0000 Subject: [PATCH 10/16] another changelog change --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5fa2076d4..c17239d0ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ The minor version will be incremented upon a breaking change and the patch versi ### Breaking - lang: Remove `state` and `interface` attributes ([#2285](https://github.com/coral-xyz/anchor/pull/2285)). -- lang: `account(zero_copy)` and `zero_copy` now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. Requires adding `bytemuck = { version = "1.4.0", features = ["min_const_generics"]}` to your `cargo.toml`. Legacy applications can use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. +- lang: `account(zero_copy)` and `zero_copy` now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes, using `repr(C)`, and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. Requires adding `bytemuck = { version = "1.4.0", features = ["min_const_generics"]}` to your `cargo.toml`. Legacy applications can use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. ## [0.26.0] - 2022-12-15 From 6eeafdd01058cb13b375c0baed56b066a125145f Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 19 Jan 2023 10:16:23 +0000 Subject: [PATCH 11/16] a bit more cleaning up --- CHANGELOG.md | 2 +- lang/attribute/account/src/lib.rs | 25 ++++++++++++++++--- tests/zero-copy/programs/zero-copy/src/lib.rs | 1 - 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c17239d0ab..76894e1922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ The minor version will be incremented upon a breaking change and the patch versi ### Breaking - lang: Remove `state` and `interface` attributes ([#2285](https://github.com/coral-xyz/anchor/pull/2285)). -- lang: `account(zero_copy)` and `zero_copy` now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes, using `repr(C)`, and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. Requires adding `bytemuck = { version = "1.4.0", features = ["min_const_generics"]}` to your `cargo.toml`. Legacy applications can use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. +- lang: `account(zero_copy)` and `zero_copy` attributes now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. This change requires adding `bytemuck = { version = "1.4.0", features = ["min_const_generics"]}` to your `cargo.toml`. Legacy applications can still use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. ## [0.26.0] - 2022-12-15 diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index d0552ca60f..c051b13c9f 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -145,7 +145,6 @@ pub fn account( if !unsafe_bytemuck { quote! { #[zero_copy] - #[derive(anchor_lang::__private::bytemuck::Pod, anchor_lang::__private::bytemuck::Zeroable)] } } else { quote! { @@ -304,13 +303,24 @@ pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::T /// A data structure that can be used as an internal field for a zero copy /// deserialized account, i.e., a struct marked with `#[account(zero_copy)]`. /// -/// This is just a convenient alias for +/// `#[zero_copy]` is just a convenient alias for /// /// ```ignore /// #[derive(Copy, Clone)] +/// #[derive(bytemuck::Zeroable)] +/// #[derive(bytemuck::Pod)] +/// #[repr(C)] +/// struct MyStruct {...} +/// ``` +/// +/// `#[zero_copy(unsafe)]` will maintain the old behaviour +/// +/// ```ignore +/// #[derive(Copy, Clone)] /// #[repr(packed)] /// struct MyStruct {...} /// ``` +/// #[proc_macro_attribute] pub fn zero_copy( args: proc_macro::TokenStream, @@ -344,7 +354,13 @@ pub fn zero_copy( let repr = match attr { Some(_attr) => quote! {}, - None => quote! {#[repr(C)]}, + None => { + if is_unsafe { + quote! {#[repr(packed)]} + } else { + quote! {#[repr(C)]} + } + }, }; let mut has_pod_attr = false; @@ -359,6 +375,9 @@ pub fn zero_copy( } } + // TODO: Despite using the full qualified path, after the derive macro is expanded + // it forces the compiler to use the local crate's bytemuck `::bytemuck::Pod`. + // Not sure how to get it to use anchor's privately exported bytemuck instead? let pod = if has_pod_attr || is_unsafe { quote! {} } else { diff --git a/tests/zero-copy/programs/zero-copy/src/lib.rs b/tests/zero-copy/programs/zero-copy/src/lib.rs index affc5db040..6c4d8e0977 100644 --- a/tests/zero-copy/programs/zero-copy/src/lib.rs +++ b/tests/zero-copy/programs/zero-copy/src/lib.rs @@ -133,7 +133,6 @@ pub struct UpdateLargeAccount<'info> { } #[account(zero_copy)] -#[repr(C)] #[derive(Default)] pub struct Foo { pub authority: Pubkey, From f68774e8eae615d403cc5e3c15328dcbb5473acd Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 19 Jan 2023 10:34:14 +0000 Subject: [PATCH 12/16] last change probably --- lang/attribute/account/src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index c051b13c9f..3cac42c59b 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -312,15 +312,6 @@ pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::T /// #[repr(C)] /// struct MyStruct {...} /// ``` -/// -/// `#[zero_copy(unsafe)]` will maintain the old behaviour -/// -/// ```ignore -/// #[derive(Copy, Clone)] -/// #[repr(packed)] -/// struct MyStruct {...} -/// ``` -/// #[proc_macro_attribute] pub fn zero_copy( args: proc_macro::TokenStream, @@ -331,6 +322,13 @@ pub fn zero_copy( match arg { proc_macro::TokenTree::Ident(ident) => { if ident.to_string() == "unsafe" { + // `#[zero_copy(unsafe)]` maintains the old behaviour + // + // ```ignore + // #[derive(Copy, Clone)] + // #[repr(packed)] + // struct MyStruct {...} + // ``` is_unsafe = true; } else { // TODO: how to return a compile error with a span (can't return prase error because expected type TokenStream) @@ -352,7 +350,8 @@ pub fn zero_copy( .iter() .find(|attr| anchor_syn::parser::tts_to_string(&attr.path) == "repr"); - let repr = match attr { + let repr = match attr { + // Users might want to manually specify repr modifiers e.g. repr(C, packed) Some(_attr) => quote! {}, None => { if is_unsafe { From 1cfbf2314c6224d878d4b8a96482f883ab278d08 Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 19 Jan 2023 10:41:33 +0000 Subject: [PATCH 13/16] done --- lang/attribute/account/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 3cac42c59b..d129946b2c 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -374,7 +374,7 @@ pub fn zero_copy( } } - // TODO: Despite using the full qualified path, after the derive macro is expanded + // TODO: Despite using the fully qualified path, after the derive macro is expanded // it forces the compiler to use the local crate's bytemuck `::bytemuck::Pod`. // Not sure how to get it to use anchor's privately exported bytemuck instead? let pod = if has_pod_attr || is_unsafe { From 9da1867e3ad0bb554f864c788dba79659661417c Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 19 Jan 2023 10:45:55 +0000 Subject: [PATCH 14/16] cargo fmt --- lang/attribute/account/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index d129946b2c..1a44d79492 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -307,7 +307,7 @@ pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::T /// /// ```ignore /// #[derive(Copy, Clone)] -/// #[derive(bytemuck::Zeroable)] +/// #[derive(bytemuck::Zeroable)] /// #[derive(bytemuck::Pod)] /// #[repr(C)] /// struct MyStruct {...} @@ -323,7 +323,7 @@ pub fn zero_copy( proc_macro::TokenTree::Ident(ident) => { if ident.to_string() == "unsafe" { // `#[zero_copy(unsafe)]` maintains the old behaviour - // + // // ```ignore // #[derive(Copy, Clone)] // #[repr(packed)] @@ -350,7 +350,7 @@ pub fn zero_copy( .iter() .find(|attr| anchor_syn::parser::tts_to_string(&attr.path) == "repr"); - let repr = match attr { + let repr = match attr { // Users might want to manually specify repr modifiers e.g. repr(C, packed) Some(_attr) => quote! {}, None => { @@ -359,7 +359,7 @@ pub fn zero_copy( } else { quote! {#[repr(C)]} } - }, + } }; let mut has_pod_attr = false; @@ -375,7 +375,7 @@ pub fn zero_copy( } // TODO: Despite using the fully qualified path, after the derive macro is expanded - // it forces the compiler to use the local crate's bytemuck `::bytemuck::Pod`. + // it forces the compiler to use the local crate's bytemuck `::bytemuck::Pod`. // Not sure how to get it to use anchor's privately exported bytemuck instead? let pod = if has_pod_attr || is_unsafe { quote! {} From 5c247617db3d045929a1e34751e5f6e20a34581e Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 19 Jan 2023 11:06:44 +0000 Subject: [PATCH 15/16] test commit, to see if local derive works ok --- CHANGELOG.md | 2 +- lang/Cargo.toml | 2 +- lang/attribute/account/src/lib.rs | 14 +++++++++----- tests/chat/programs/chat/Cargo.toml | 2 +- tests/misc/programs/misc-optional/Cargo.toml | 2 +- tests/misc/programs/misc/Cargo.toml | 2 +- tests/zero-copy/programs/zero-copy/Cargo.toml | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76894e1922..4bafef9237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ The minor version will be incremented upon a breaking change and the patch versi ### Breaking - lang: Remove `state` and `interface` attributes ([#2285](https://github.com/coral-xyz/anchor/pull/2285)). -- lang: `account(zero_copy)` and `zero_copy` attributes now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. This change requires adding `bytemuck = { version = "1.4.0", features = ["min_const_generics"]}` to your `cargo.toml`. Legacy applications can still use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. +- lang: `account(zero_copy)` and `zero_copy` attributes now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. This change requires adding `bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"]}` to your `cargo.toml`. Legacy applications can still use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. ## [0.26.0] - 2022-12-15 diff --git a/lang/Cargo.toml b/lang/Cargo.toml index bb4328c7ad..5901be7f0c 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -35,7 +35,7 @@ anchor-derive-accounts = { path = "./derive/accounts", version = "0.26.0" } arrayref = "0.3.6" base64 = "0.13.0" borsh = "0.9" -bytemuck = { version = "1.4.0", features = ["derive"]} +bytemuck = "1.4.0" solana-program = "1.13.5" thiserror = "1.0.20" bincode = "1.3.3" diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index 1a44d79492..a1af4b854f 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -59,6 +59,9 @@ mod id; /// [`Pod`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html). Please review the /// [`safety`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html#safety) /// section before using. +/// +/// Using `zero_copy` requires adding the following to your `cargo.toml` file: +/// `bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"]}` #[proc_macro_attribute] pub fn account( args: proc_macro::TokenStream, @@ -374,18 +377,19 @@ pub fn zero_copy( } } - // TODO: Despite using the fully qualified path, after the derive macro is expanded - // it forces the compiler to use the local crate's bytemuck `::bytemuck::Pod`. - // Not sure how to get it to use anchor's privately exported bytemuck instead? + // Once the Pod derive macro is expanded the compiler has to use the local crate's + // bytemuck `::bytemuck::Pod` anyway, so we're no longer using the privately + // exported anchor bytemuck `__private::bytemuck`, so that there won't be any + // possible disparity between the anchor version and the local crate's version. let pod = if has_pod_attr || is_unsafe { quote! {} } else { - quote! {#[derive(anchor_lang::__private::bytemuck::Pod)]} + quote! {#[derive(::bytemuck::Pod)]} }; let zeroable = if has_zeroable_attr || is_unsafe { quote! {} } else { - quote! {#[derive(anchor_lang::__private::bytemuck::Zeroable)]} + quote! {#[derive(::bytemuck::Zeroable)]} }; proc_macro::TokenStream::from(quote! { diff --git a/tests/chat/programs/chat/Cargo.toml b/tests/chat/programs/chat/Cargo.toml index b7da46c292..6cb9e571b3 100644 --- a/tests/chat/programs/chat/Cargo.toml +++ b/tests/chat/programs/chat/Cargo.toml @@ -17,4 +17,4 @@ default = [] [dependencies] anchor-lang = { path = "../../../../lang" } -bytemuck = {version = "1.4.0", features = ["min_const_generics"]} +bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]} diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml index af9c2e20de..2461dc2684 100644 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -19,4 +19,4 @@ default = [] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } spl-associated-token-account = "1.1.1" -bytemuck = {version = "1.4.0", features = ["min_const_generics"]} +bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]} diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml index faf03ecbb5..500c850b16 100644 --- a/tests/misc/programs/misc/Cargo.toml +++ b/tests/misc/programs/misc/Cargo.toml @@ -19,4 +19,4 @@ default = [] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } spl-associated-token-account = "1.1.1" -bytemuck = {version = "1.4.0", features = ["min_const_generics"]} +bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]} diff --git a/tests/zero-copy/programs/zero-copy/Cargo.toml b/tests/zero-copy/programs/zero-copy/Cargo.toml index 8ca06ebf36..73869bc9fc 100644 --- a/tests/zero-copy/programs/zero-copy/Cargo.toml +++ b/tests/zero-copy/programs/zero-copy/Cargo.toml @@ -18,7 +18,7 @@ test-bpf = [] [dependencies] anchor-lang = { path = "../../../../lang" } -bytemuck = {version = "1.4.0", features = ["min_const_generics"]} +bytemuck = {version = "1.4.0", features = ["derive", "min_const_generics"]} [dev-dependencies] anchor-client = { path = "../../../../client", features = ["debug"] } From e7cd08a58b8ed9f27da1be70cf78a839de326d75 Mon Sep 17 00:00:00 2001 From: henrye Date: Fri, 20 Jan 2023 10:09:59 +0000 Subject: [PATCH 16/16] cargo fmt --- lang/attribute/account/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs index a1af4b854f..56ca2f1854 100644 --- a/lang/attribute/account/src/lib.rs +++ b/lang/attribute/account/src/lib.rs @@ -60,7 +60,7 @@ mod id; /// [`safety`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html#safety) /// section before using. /// -/// Using `zero_copy` requires adding the following to your `cargo.toml` file: +/// Using `zero_copy` requires adding the following to your `cargo.toml` file: /// `bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"]}` #[proc_macro_attribute] pub fn account(