-
Notifications
You must be signed in to change notification settings - Fork 137
[pointer] Add generic projection/cast framework #2860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Summary of ChangesHello @joshlf, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a foundational generic framework for pointer projection and casting within the Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2860 +/- ##
==========================================
- Coverage 91.94% 91.90% -0.05%
==========================================
Files 20 20
Lines 5824 5778 -46
==========================================
- Hits 5355 5310 -45
+ Misses 469 468 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a significant and well-designed refactoring of the pointer casting and projection framework by adding the Project and Cast traits. This unification simplifies the codebase and makes the casting logic more extensible and type-driven. The changes across the files are consistent with this new design.
My review focuses on the correctness and safety of these new abstractions. I've found a few places where // SAFETY: TODO comments indicate missing safety justifications for unsafe blocks. It's important to fill these in to ensure the soundness of the library. Most notably, I've identified one InvariantsEq implementation in zerocopy-derive/src/enum.rs that appears to be incorrect and could lead to unsoundness. Please address these points to finalize this excellent refactoring.
| // SAFETY: TODO | ||
| unsafe { Ptr::from_inner(ptr) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The safety justification for this unsafe block is missing. Please add a SAFETY comment explaining why it's sound to create a Ptr from the result of the projection.
A proper justification would likely involve reasoning about how the invariants of Ptr are upheld:
- The aliasing invariant
I::Aliasingis preserved because field projection does not introduce new aliasing concerns. - The alignment invariant is relaxed to
Unaligned, which is always safe. - The validity invariant
I::Validityis preserved due to theT::Type: CastableFrom<T, I::Validity, I::Validity>bound. - The inner
PtrInnerinvariants (related to provenance and lifetime) are upheld by the contract ofHasField::project.
| // SAFETY: TODO | ||
| unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<#variant_struct_ident #ty_generics> for ___ZerocopyVariants #ty_generics #where_clause {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A safety justification is needed for this InvariantsEq implementation. Please add a SAFETY comment explaining why it's sound to have &___ZerocopyVariants and &___ZerocopyVariantStruct pointing to the same memory simultaneously. The justification should cover that ___ZerocopyVariants is a union containing the variant struct, and accessing it is done through controlled means within the derive logic, preventing misuse.
| // SAFETY: TODO | ||
| unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This unsafe impl is missing its safety justification. Please add a SAFETY comment explaining why InvariantsEq<___ZerocopyRawEnum, Self> holds. The justification should detail how ___ZerocopyRawEnum is a layout-compatible representation of the enum Self, and therefore having references to both for the same memory region is safe under the controlled conditions of the derive macro.
zerocopy-derive/src/enum.rs
Outdated
| // SAFETY: TODO | ||
| unsafe impl ::zerocopy::Immutable for ___ZerocopyTag { | ||
| fn only_derive_is_allowed_to_implement_this_trait() {} | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a safety justification for this Immutable implementation. ___ZerocopyTag is an enum with a primitive representation and no fields, so it contains no UnsafeCells, making it Immutable. A brief SAFETY comment to this effect would be appropriate.
| // SAFETY: TODO | |
| unsafe impl ::zerocopy::Immutable for ___ZerocopyTag { | |
| fn only_derive_is_allowed_to_implement_this_trait() {} | |
| } | |
| // SAFETY: `___ZerocopyTag` is a `repr(Int)` enum with no fields, and | |
| // thus contains no `UnsafeCell`s. | |
| unsafe impl ::zerocopy::Immutable for ___ZerocopyTag { | |
| fn only_derive_is_allowed_to_implement_this_trait() {} | |
| } |
01addfc to
f291309
Compare
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a generic framework for pointer projections and casts using the new Project and Cast traits. This is a significant and valuable refactoring that unifies various casting and projection mechanisms throughout the codebase, making them more composable and robust. The changes are extensive, touching many core parts of the pointer manipulation logic.
While the overall direction is excellent, I've found several critical issues related to TODOs in SAFETY comments, particularly concerning potential soundness holes with InvariantsEq implementations for enums and a missing safety justification for a new public project method. These must be addressed to ensure the soundness guarantees of zerocopy are upheld. I've also pointed out a few other places where TODOs in safety comments should be replaced with proper justifications.
| // SAFETY: TODO | ||
| unsafe { Ptr::from_inner(ptr) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SAFETY comment for this unsafe block is a TODO. This is a public function, and the soundness of its unsafe usage must be thoroughly justified. The current implementation relies on T::Type: CastableFrom<T, I::Validity, I::Validity>, but it's not clear how this ensures aliasing invariants are maintained, especially since HasField::project could project to a field with different aliasing properties (e.g., a field containing an UnsafeCell inside a struct that is otherwise Immutable). Please provide a full safety justification or consider making this function unsafe.
| let invariants_eq_impl: TokenStream = parse_quote! { | ||
| // SAFETY: TODO | ||
| // | ||
| // TODO: This might not actually be sound – it's possible for there | ||
| // to be `UnsafeCell`s in some but not all enum variants. | ||
| unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<#variant_struct_ident #ty_generics> for ___ZerocopyVariants #ty_generics #where_clause {} | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This SAFETY comment contains a TODO and a note that this implementation might be unsound. This is a major soundness concern. If different enum variants have different UnsafeCell layouts, implementing InvariantsEq for the union of variant structs (___ZerocopyVariants) could lead to aliasing violations and undefined behavior. This must be resolved before merging. Either the implementation needs to be proven sound, or it needs to be guarded by stronger trait bounds or removed.
| let invariants_eq_impl = quote! { | ||
| // SAFETY: TODO | ||
| unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {} | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zerocopy-derive/src/enum.rs
Outdated
| // SAFETY: TODO | ||
| unsafe impl ::zerocopy::Immutable for ___ZerocopyTag { | ||
| fn only_derive_is_allowed_to_implement_this_trait() {} | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This SAFETY comment is a TODO. While this Immutable implementation for ___ZerocopyTag seems correct because it's a C-like enum with no fields and thus cannot contain an UnsafeCell, the safety justification should be explicitly written out rather than leaving a TODO.
| // SAFETY: TODO | |
| unsafe impl ::zerocopy::Immutable for ___ZerocopyTag { | |
| fn only_derive_is_allowed_to_implement_this_trait() {} | |
| } | |
| // SAFETY: `___ZerocopyTag` is a C-like enum with no fields, so it cannot | |
| // contain an `UnsafeCell`. | |
| unsafe impl ::zerocopy::Immutable for ___ZerocopyTag { | |
| fn only_derive_is_allowed_to_implement_this_trait() {} | |
| } |
| // SAFETY: TODO | ||
| unsafe impl #generics #zerocopy_crate::HasField<(), { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(tag) }> for ___ZerocopyRawEnum #ty_generics { | ||
| fn only_derive_is_allowed_to_implement_this_trait() {} | ||
|
|
||
| type Type = ___ZerocopyTag; | ||
|
|
||
| #[inline(always)] | ||
| unsafe fn project(slf: *mut Self) -> *mut Self::Type { | ||
| slf.cast() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This SAFETY comment is a TODO. The implementation seems sound because tag is the first field of a #[repr(C)] struct, making a pointer cast valid for projection. Please add the explicit safety justification.
| // SAFETY: TODO | |
| unsafe impl #generics #zerocopy_crate::HasField<(), { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(tag) }> for ___ZerocopyRawEnum #ty_generics { | |
| fn only_derive_is_allowed_to_implement_this_trait() {} | |
| type Type = ___ZerocopyTag; | |
| #[inline(always)] | |
| unsafe fn project(slf: *mut Self) -> *mut Self::Type { | |
| slf.cast() | |
| } | |
| } | |
| // SAFETY: `___ZerocopyRawEnum` is a `#[repr(C)]` struct and `tag` is its | |
| // first field, so a pointer cast is a valid projection. | |
| unsafe impl #generics #zerocopy_crate::HasField<(), { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(tag) }> for ___ZerocopyRawEnum #ty_generics { | |
| fn only_derive_is_allowed_to_implement_this_trait() {} | |
| type Type = ___ZerocopyTag; | |
| #[inline(always)] | |
| unsafe fn project(slf: *mut Self) -> *mut Self::Type { | |
| slf.cast() | |
| } | |
| } |
Add `unsafe trait Project` and `unsafe trait Cast: Project`. `Cast` is implemented for any address-preserving cast, while `Project` generalizes to conversions which may not preserve the address of the referent (ie, field projections). Use these (mostly `Cast`, but some `Project`) to unify `PtrInner`/`Ptr` casts, field projections, and `SizeEq` casts. Replace a good amount of unsafe derive-generated code with uses of this machinery. Makes progress on #196 Closes #2856 gherrit-pr-id: Gdeb4f5a282b778a82175703218739a93074d0cc4
f291309 to
f675ef9
Compare
| // | ||
| // TODO: This might not actually be sound – it's possible for there | ||
| // to be `UnsafeCell`s in some but not all enum variants. | ||
| unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<#variant_struct_ident #ty_generics> for ___ZerocopyVariants #ty_generics #where_clause {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to just replace BecauseInvariantsEq with BecauseImmutable and then impl Immutable for ___ZerocopyVariants and each___ZerocopyVariantStruct_Xxx. We currently require all unions to be Immutable, so this should be fine – we can just add an OuterEnum: Immutable bound to each Immutable impl block (where OuterEnum is the name of the type being derived on).
Add
unsafe trait Projectandunsafe trait Cast: Project.Castisimplemented for any address-preserving cast, while
Projectgeneralizesto conversions which may not preserve the address of the referent (ie,
field projections). Use these (mostly
Cast, but someProject) tounify
PtrInner/Ptrcasts, field projections, andSizeEqcasts.Replace a good amount of unsafe derive-generated code with uses of this
machinery.
Makes progress on #196
Closes #2856
Latest Update: v4 — Compare vs v3
📚 Full Patch History
Links show the diff between the row version and the column version.