├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bors.toml ├── derive ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── rustfmt.toml ├── src └── lib.rs └── tests └── refcells.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | matrix: 3 | include: 4 | - name: 1.31.0, core only (Linux) 5 | rust: 1.31.0 6 | os: linux 7 | env: FEATURES="" 8 | - name: 1.31.0 (Linux) 9 | rust: 1.31.0 10 | os: linux 11 | env: FEATURES="--features std" 12 | - name: Stable (Linux) 13 | rust: stable 14 | os: linux 15 | env: FEATURES="--features std" 16 | - name: Stable (macOS) 17 | rust: stable 18 | os: osx 19 | env: FEATURES="--features std" 20 | - name: Stable (Windows) 21 | rust: stable 22 | os: windows 23 | env: FEATURES="--features std" 24 | - name: Beta (Linux) 25 | rust: beta 26 | os: linux 27 | env: FEATURES="--features std" 28 | - name: Nightly (Linux) 29 | rust: nightly 30 | os: linux 31 | env: FEATURES="--features std" 32 | branches: 33 | only: 34 | - staging # bors r+ 35 | - trying # bors try 36 | script: cargo test $FEATURES 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust.features": ["std"], 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # inert_derive 0.1.4 2 | 3 | * Silenced a `dead_code` warning for the `value` field of generated wrappers. 4 | 5 | # 0.3.0 6 | 7 | ## Highlight 8 | 9 | `RefCell` now neutralizes to `InertRefCell`, whose `borrow` method will 10 | panic if the `RefCell` was borrowed mutably prior to being neutralized. 11 | 12 | ## Other breaking changes 13 | 14 | * Made more types neutralize to `Inert` to access non-`NeutralizeUnsafe` `T`s. 15 | * Removed `InertPanicInfo`. 16 | 17 | # 0.2.4 18 | 19 | The most notable change in this release is that impls of `NeutralizeUnsafe` for 20 | things like `(A, B)` don't require `A` to be `NeutralizeUnsafe` itself anymore, 21 | which means that `(A, B)` can be neutralised even if some of its fields aren't. 22 | 23 | Furthermore, if those fields aren't `NeutralizeUnsafe` but are `Sync`, users 24 | can still safely access their contents through `Inert::get_ref`. 25 | 26 | ## Other changes 27 | 28 | * Made the getters be `#[inline]`, thanks Rémy Rakić! 29 | * Fixed a span which would make the code not build with `#[deny(unsafe_code)]`. 30 | 31 | # inert_derive 0.1.2 32 | 33 | * Decorated the inert getters with `#[allow(unsafe_code)]`. 34 | 35 | # 0.2.3 36 | 37 | * Introduced `inert::Neutralized`. 38 | * Implemented `#[inert::neutralize(as vis? unsafe InertFoo)]`. 39 | 40 | # 0.2.2 41 | 42 | * Implemented `#[inert::neutralize(as Self)]`. 43 | 44 | # 0.2.1 45 | 46 | * Tweaked some documentation. 47 | * Added a test based on a tree of `RefCell` values. 48 | 49 | # 0.2.0 50 | 51 | Let's try to document a bit what the hell we are doing here. 👀 52 | 53 | ## Breaking changes 54 | 55 | * Renamed `Neutralize` to `NeutralizeUnsafe`. 56 | * Renamed `Inert::from_ref` to `Inert::new_unchecked`. 57 | 58 | ## Non-breaking changes 59 | 60 | * Added some documentation. 61 | * Implemented `Eq`, `Ord`, `Debug` and `Display` for `Inert`. 62 | * Implemented `NeutralizeUnsafe` for `core::cell::Cell`. 63 | * Implemented `NeutralizeUnsafe` for `std::sync::Weak`. 64 | * Introduced safe ways to neutralize with `Inert::new` and `Inert::new_mut`. 65 | 66 | # 0.1.0 67 | 68 | Initial release! No documentation, no tests, only 🥖 69 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inert" 3 | version = "0.3.0" # update `documentation` link on bump 4 | authors = ["Anthony Ramine "] 5 | edition = "2018" 6 | description = "Inert lets you use non-Sync values in a Sync way" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/nox/inert" 9 | documentation = "https://docs.rs/inert/0.3.0/" 10 | categories = ["concurrency", "data-structures"] 11 | keywords = ["sync", "thread"] 12 | 13 | [package.metadata.docs.rs] 14 | features = ["std"] 15 | 16 | [features] 17 | std = [] 18 | 19 | [lib] 20 | test = false 21 | 22 | [dependencies] 23 | inert_derive = {version = "0.1.3", path = "derive"} 24 | 25 | [workspace] 26 | members = [ 27 | "derive", 28 | ] 29 | 30 | [[test]] 31 | name = "refcells" 32 | required-features = ["std"] 33 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2018 The Rust Project Developers 2 | Copyright (c) 2018 Anthony Ramine 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inert 2 | 3 | This is a mechanism to access non-`Sync` values in a `Sync` way. 4 | 5 | ## How is this sound? 6 | 7 | When the user creates a `&Inert` value from a `&T`, they must swear on the 8 | holy baguette that they won't use the `T` directly until all the `Inert` 9 | wrappers go away, while the various implementations of the `NeutralizeUnsafe` 10 | trait make sure that the non-`Sync` behaviour of the `T` cannot be observed 11 | through the wrapper. 12 | 13 | ## How can I help? 14 | 15 | Improve documentation, test the crate, make use of it. 16 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push", 3 | ] 4 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inert_derive" 3 | version = "0.1.4" # update `documentation` link on bump 4 | authors = ["Anthony Ramine "] 5 | edition = "2018" 6 | description = "See the inert crate" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/nox/inert" 9 | documentation = "https://docs.rs/inert_derive/0.1.4/" 10 | categories = ["concurrency", "data-structures"] 11 | keywords = ["sync", "thread"] 12 | 13 | [lib] 14 | proc-macro = true 15 | test = false 16 | 17 | [dependencies] 18 | proc-macro2 = "0.4.27" 19 | quote = "0.6.11" 20 | 21 | [dependencies.syn] 22 | version = "0.15.27" 23 | default-features = false 24 | features = ["derive", "parsing", "printing", "proc-macro"] 25 | -------------------------------------------------------------------------------- /derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro2::TokenStream; 4 | use quote::{quote, quote_spanned}; 5 | use syn::parse::{Error, Parse, ParseStream}; 6 | use syn::spanned::Spanned; 7 | use syn::token::{Brace, Bracket, Paren}; 8 | use syn::{ 9 | self, Attribute, Data, DeriveInput, Field, Fields, GenericParam, Ident, Path, Token, Visibility, 10 | }; 11 | 12 | /// Derives neutralizing traits. 13 | /// 14 | /// There are two ways to use this procedural macro attribute. 15 | /// 16 | /// ### `#[inert::neutralize(as Self)]` 17 | /// 18 | /// Implements `NeutralizeUnsafe` with `type Output = Self;`. Given that this 19 | /// can type check if and only if `Self` is `Sync`, the traits `Neutralize` 20 | /// and `NeutralizeMut` are also automatically implemented. 21 | /// 22 | /// ### `#[inert::neutralize(as vis? unsafe InertFoo)]` 23 | /// 24 | /// Implements `NeutralizeUnsafe` with `type Output = InertFoo;`. `InertFoo` is 25 | /// a wrapper generated by the macro itself, its visibility is controlled 26 | /// by `vis`. A getter method named is generated for each field adorned with 27 | /// `#[inert::field(vis? ident?)]`, if `ident` is missing, the getter is given 28 | /// the same name as the field itself. 29 | /// 30 | /// Given the following `Node` type: 31 | /// 32 | /// ```ignore 33 | /// #[inert::neutralize(as pub(crate) unsafe InertNode)] 34 | /// struct Node { 35 | /// #[inert::field(pub(crate) tag)] 36 | /// name: String, 37 | /// #[inert::field(pub(crate))] 38 | /// attributes: Vec, 39 | /// #[inert::field] 40 | /// children: RefCell>, 41 | /// } 42 | /// ``` 43 | /// 44 | /// The following code will be generated: 45 | /// 46 | /// ```ignore 47 | /// pub(crate) struct InertNode { 48 | /// value: inert::Neutralized, 49 | /// } 50 | /// 51 | /// unsafe impl NeutralizeUnsafe for Node { 52 | /// type Output = InertNode; 53 | /// 54 | /// unsafe fn neutralize_unsafe(&self) -> &InertNode { 55 | /// &*(self as *const Self as *const Self::Output) 56 | /// } 57 | /// } 58 | /// 59 | /// impl InertNode { 60 | /// pub(crate) fn tag(&self) -> &Inert { 61 | /// unsafe { Inert::new_unchecked(&self.name) } 62 | /// } 63 | /// 64 | /// pub(crate) fn attributes(&self) -> &Inert> { 65 | /// unsafe { Inert::new_unchecked(&self.attributes) } 66 | /// } 67 | /// 68 | /// fn children(&self) -> &Inert>> { 69 | /// unsafe { Inert::new_unchecked(&self.children) } 70 | /// } 71 | /// } 72 | /// ``` 73 | #[proc_macro_attribute] 74 | pub fn neutralize( 75 | attr: proc_macro::TokenStream, 76 | item: proc_macro::TokenStream, 77 | ) -> proc_macro::TokenStream { 78 | let attr = syn::parse_macro_input!(attr as NeutralizeAttr); 79 | let input = syn::parse_macro_input!(item as DeriveInput); 80 | match attr { 81 | NeutralizeAttr::AsSelf(self_type) => neutralize_as_self(self_type, input), 82 | NeutralizeAttr::AsWrapper(wrapper) => { 83 | neutralize_as_wrapper(wrapper, input).unwrap_or_else(|e| e.to_compile_error().into()) 84 | }, 85 | }.into() 86 | } 87 | 88 | enum NeutralizeAttr { 89 | AsSelf(Token![Self]), 90 | AsWrapper(Wrapper), 91 | } 92 | 93 | impl Parse for NeutralizeAttr { 94 | fn parse(input: ParseStream) -> Result { 95 | input.parse::()?; 96 | if input.peek(Token![Self]) { 97 | return input.parse::().map(NeutralizeAttr::AsSelf); 98 | } 99 | input.parse().map(NeutralizeAttr::AsWrapper) 100 | } 101 | } 102 | 103 | struct Wrapper { 104 | vis: Visibility, 105 | name: Ident, 106 | } 107 | 108 | impl Parse for Wrapper { 109 | fn parse(input: ParseStream) -> Result { 110 | let vis = input.parse()?; 111 | input.parse::()?; 112 | let name = input.parse()?; 113 | Ok(Self { vis, name }) 114 | } 115 | } 116 | 117 | // FIXME(nox): https://github.com/dtolnay/syn/issues/589 118 | struct AttrArgument { 119 | argument: T, 120 | } 121 | 122 | // FIXME(nox): https://github.com/dtolnay/syn/issues/589 123 | impl Parse for AttrArgument 124 | where 125 | T: Parse, 126 | { 127 | fn parse(input: ParseStream) -> Result { 128 | let content; 129 | if input.peek(Paren) { 130 | syn::parenthesized!(content in input); 131 | } else if input.peek(Brace) { 132 | syn::braced!(content in input); 133 | } else if input.peek(Bracket) { 134 | syn::bracketed!(content in input); 135 | } else { 136 | return Ok(Self { argument: syn::parse2(quote! {})? }); 137 | } 138 | let argument = T::parse(&content)?; 139 | Ok(Self { argument }) 140 | } 141 | } 142 | 143 | struct FieldAttr { 144 | vis: Visibility, 145 | name: Option, 146 | } 147 | 148 | impl Parse for FieldAttr { 149 | fn parse(input: ParseStream) -> Result { 150 | let vis = input.parse()?; 151 | let name = input.parse()?; 152 | Ok(Self { vis, name }) 153 | } 154 | } 155 | 156 | fn neutralize_as_self(self_type: Token![Self], input: DeriveInput) -> TokenStream { 157 | let type_name = &input.ident; 158 | let (impl_gen, ty_gen, where_) = input.generics.split_for_impl(); 159 | 160 | // This is supposed to improve "trait bound is not satisfied" errors 161 | // in case Self is !Sync, but this does not actually work yet. 162 | // 163 | // https://github.com/rust-lang/rust/issues/41817 164 | let output = quote_spanned! {self_type.span()=> type Output = Self; }; 165 | 166 | quote! { 167 | #input 168 | 169 | unsafe impl #impl_gen ::inert::Neutralize for #type_name #ty_gen #where_ {} 170 | unsafe impl #impl_gen ::inert::NeutralizeMut for #type_name #ty_gen #where_ {} 171 | 172 | unsafe impl #impl_gen ::inert::NeutralizeUnsafe for #type_name #ty_gen #where_ { 173 | #output 174 | 175 | #[inline] 176 | unsafe fn neutralize_unsafe(&self) -> &Self { self } 177 | } 178 | } 179 | } 180 | 181 | fn neutralize_as_wrapper(wrapper: Wrapper, mut input: DeriveInput) -> Result { 182 | check_params(&input)?; 183 | 184 | let methods = inert_methods(struct_fields_mut(&mut input)?)?; 185 | 186 | let Wrapper { vis, name } = wrapper; 187 | let type_name = &input.ident; 188 | let (impl_gen, ty_gen, where_) = input.generics.split_for_impl(); 189 | 190 | let wrapper = quote_spanned! {name.span()=> 191 | #vis struct #name #ty_gen #where_ { 192 | #[allow(dead_code)] 193 | value: ::inert::Neutralized<#type_name #ty_gen>, 194 | } 195 | }; 196 | 197 | Ok(quote! { 198 | #input 199 | 200 | #wrapper 201 | 202 | unsafe impl #impl_gen ::inert::NeutralizeUnsafe for #type_name #ty_gen #where_ { 203 | type Output = #name #ty_gen; 204 | 205 | #[inline] 206 | unsafe fn neutralize_unsafe(&self) -> &#name #ty_gen { 207 | &*(self as *const Self as *const Self::Output) 208 | } 209 | } 210 | 211 | impl #impl_gen #name #ty_gen #where_ { 212 | #methods 213 | } 214 | }) 215 | } 216 | 217 | fn check_params(input: &DeriveInput) -> Result<(), Error> { 218 | for param in &input.generics.params { 219 | if let GenericParam::Lifetime(_) = *param { 220 | continue; 221 | } 222 | return Err(Error::new( 223 | param.span(), 224 | "cannot automatically neutralize a parameterized type", 225 | )); 226 | } 227 | Ok(()) 228 | } 229 | 230 | fn struct_fields_mut(input: &mut DeriveInput) -> Result, Error> { 231 | // FIXME(nox): https://github.com/rust-lang/rust/issues/58910 232 | let span = input.span(); 233 | if let Data::Struct(ref mut data_struct) = input.data { 234 | if let Fields::Named(ref mut fields_named) = data_struct.fields { 235 | return Ok(fields_named.named.iter_mut()); 236 | } 237 | } 238 | Err(Error::new(span, "only structs can be automatically neutralized")) 239 | } 240 | 241 | #[allow(dead_code)] 242 | fn inert_methods<'input>( 243 | fields: impl Iterator, 244 | ) -> Result { 245 | let mut methods = quote! {}; 246 | 247 | for field in fields { 248 | let attr = match extract_inert_field(field)? { 249 | Some(attr) => attr, 250 | None => continue, 251 | }; 252 | 253 | let field_name = field.ident.as_ref().expect("named field"); 254 | let ty = &field.ty; 255 | let FieldAttr { vis, name } = attr; 256 | let getter_name = name.as_ref().unwrap_or(field_name); 257 | 258 | methods.extend(quote_spanned! {field.ty.span()=> 259 | #[allow(unsafe_code)] 260 | #[inline] 261 | #vis fn #getter_name(&self) -> &inert::Inert<#ty> { 262 | unsafe { inert::Inert::new_unchecked(&self.value.as_ref().#field_name) } 263 | } 264 | }); 265 | } 266 | 267 | Ok(methods) 268 | } 269 | 270 | fn extract_inert_field(field: &mut Field) -> Result, Error> { 271 | let (pos, attr) = if let Some(pos) = find_inert_field_attr(&field.attrs) { 272 | (pos, field.attrs.remove(pos)) 273 | } else { 274 | return Ok(None); 275 | }; 276 | 277 | let attr = syn::parse2::>(attr.tts)?.argument; 278 | 279 | if let Some(dupe_pos) = find_inert_field_attr(&field.attrs[pos..]) { 280 | return Err(Error::new( 281 | field.attrs[pos..][dupe_pos].span(), 282 | "multiple #[inert::field] attributes found on the same field", 283 | )); 284 | } 285 | 286 | Ok(Some(attr)) 287 | } 288 | 289 | fn find_inert_field_attr<'input>( 290 | attrs: impl IntoIterator, 291 | ) -> Option { 292 | attrs.into_iter().position(|attr| is_simple_path(&attr.path, &["inert", "field"])) 293 | } 294 | 295 | fn is_simple_path(path: &Path, idents: &[&str]) -> bool { 296 | let mut segments = path.segments.iter(); 297 | let mut idents = idents.iter(); 298 | segments.by_ref().zip(idents.by_ref()).all(|(s, i)| s.ident == *i && s.arguments.is_empty()) 299 | && segments.next().is_none() 300 | && idents.next().is_none() 301 | } 302 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | force_multiline_blocks = true 2 | match_block_trailing_comma = true 3 | reorder_imports = true 4 | use_small_heuristics = "Max" 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Anthony Ramine 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! `Inert` lets you access non-`Sync` data in `Sync` context, hiding from the 10 | //! user anything that may not be sound to use when the value is shared. 11 | 12 | #![cfg_attr(not(feature = "std"), no_std)] 13 | 14 | #[doc(inline)] 15 | pub use inert_derive::neutralize; 16 | 17 | #[cfg(feature = "std")] 18 | extern crate core; 19 | 20 | use core::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; 21 | use core::cmp::Ordering; 22 | use core::fmt; 23 | use core::marker::PhantomData; 24 | use core::ops::Deref; 25 | 26 | #[cfg(feature = "std")] 27 | use std::borrow::Cow; 28 | #[cfg(feature = "std")] 29 | use std::rc::Rc; 30 | 31 | /// Marker trait for types that can be safely neutralized. 32 | pub unsafe trait Neutralize: NeutralizeMut {} 33 | 34 | /// Marker trait for types that can be safely neutralized mutably. 35 | pub unsafe trait NeutralizeMut: NeutralizeUnsafe {} 36 | 37 | /// Unsafely neutralizes a reference, returning a `Sync` view to it. 38 | /// 39 | /// For example, `RefCell` implements it with `Output` as `T::Output`, 40 | /// accessing the inner value of the cell directly through `RefCell::as_ptr`. 41 | pub unsafe trait NeutralizeUnsafe { 42 | /// The type of the `Sync` view. 43 | type Output: ?Sized + Sync; 44 | 45 | /// Unsafely neutralizes `self`. 46 | /// 47 | /// # Safety 48 | /// 49 | /// It is undefined behaviour to use `self` as long as any thread is 50 | /// still doing something with the return value of that method. 51 | unsafe fn neutralize_unsafe(&self) -> &Self::Output; 52 | } 53 | 54 | /// A wrapper for neutralized values. 55 | /// 56 | /// If `T` is `Neutralize`, then it can be safely neutralized from an 57 | /// immutable reference. Almost everything implements `Neutralize` but 58 | /// `RefCell` (and similar types). 59 | /// 60 | /// If `T` is `NeutralizeMut`, then it can be safely neutralized from a mutable 61 | /// reference. Almost everything implements `NeutralizeMut` but `Rc>` 62 | /// and `&'a RefCell` (and similar types). 63 | /// 64 | /// If `T` is `NeutralizeUnsafe`, this type derefs to `T::Output`, 65 | /// with no safe way to reach out for the `T` value itself, which is why 66 | /// it is sound for `Inert` to be `Sync`. 67 | #[repr(transparent)] 68 | pub struct Inert { 69 | value: Neutralized, 70 | } 71 | unsafe impl Sync for Inert where T: ?Sized {} 72 | 73 | impl Inert 74 | where 75 | T: ?Sized, 76 | { 77 | /// Creates a new `Inert` from a reference. 78 | #[inline] 79 | pub fn new(value: &T) -> &Self 80 | where 81 | T: Neutralize, 82 | { 83 | unsafe { Self::new_unchecked(value) } 84 | } 85 | 86 | /// Creates a new `Inert` from a mutable reference. 87 | #[inline] 88 | pub fn new_mut(value: &mut T) -> &Self 89 | where 90 | T: NeutralizeMut, 91 | { 92 | unsafe { Self::new_unchecked(value) } 93 | } 94 | 95 | /// Unsafely creates a new `Inert` from a reference. 96 | /// 97 | /// # Safety 98 | /// 99 | /// The user must swear on the holy baguette that they won't do anything 100 | /// with the `&T` as long as any thread is still doing things with the 101 | /// `&Inert`, either directly or through other neutralized values 102 | /// reached through the inner `&T::Output` value, lest they provoke 103 | /// undefined behaviour, or worse, spoil their entire wheat harvest. 104 | #[inline] 105 | pub unsafe fn new_unchecked(value: &T) -> &Self { 106 | &*(value as *const T as *const Self) 107 | } 108 | 109 | /// Returns a reference to the inner `T`. 110 | #[inline] 111 | pub fn get_ref(this: &Self) -> &T 112 | where 113 | T: Sync, 114 | { 115 | unsafe { Self::get_ref_unchecked(this) } 116 | } 117 | 118 | /// Unsafely returns a reference to the inner `T`. 119 | /// 120 | /// # Safety 121 | /// 122 | /// Undefined behaviour is possible if `T` is not `Sync`. 123 | #[inline] 124 | pub unsafe fn get_ref_unchecked(this: &Self) -> &T { 125 | this.value.as_ref() 126 | } 127 | } 128 | 129 | impl Deref for Inert 130 | where 131 | T: ?Sized + NeutralizeUnsafe, 132 | { 133 | type Target = T::Output; 134 | 135 | #[inline] 136 | fn deref(&self) -> &Self::Target { 137 | unsafe { Self::get_ref_unchecked(self).neutralize_unsafe() } 138 | } 139 | } 140 | 141 | /// A neutralized value. 142 | /// 143 | /// Used by `#[inert::neutralize(as unsafe Foo)]`, shouldn't be needed directly. 144 | /// Helpful nonetheless to ensure the `T` value cannot be safely accessed. 145 | #[repr(transparent)] 146 | pub struct Neutralized { 147 | _marker: PhantomData<*mut ()>, 148 | value: T, 149 | } 150 | unsafe impl Sync for Neutralized where T: ?Sized {} 151 | 152 | impl Neutralized 153 | where 154 | T: ?Sized, 155 | { 156 | /// Unsafely returns a reference to the inner `T` value. 157 | #[inline] 158 | pub unsafe fn as_ref(&self) -> &T { 159 | &self.value 160 | } 161 | } 162 | 163 | // Obviously everything in this crate is unsafe so it needs serious scrutiny, 164 | // but this part especially is where all the magic happens. 165 | // 166 | // The following implementations are how the two safe methods `Inert::new` and 167 | // `Inert::new_mut` are constrained to actually be sound. 168 | 169 | unsafe impl<'a, T> NeutralizeMut for &'a T where T: ?Sized + Neutralize {} 170 | unsafe impl<'a, T> Neutralize for &'a T where T: ?Sized + Neutralize {} 171 | 172 | unsafe impl<'a, T> NeutralizeMut for &'a mut T where T: ?Sized + NeutralizeMut {} 173 | unsafe impl<'a, T> Neutralize for &'a mut T where T: ?Sized + Neutralize {} 174 | 175 | unsafe impl NeutralizeMut for Cell where T: ?Sized + NeutralizeMut {} 176 | 177 | unsafe impl NeutralizeMut for RefCell where T: ?Sized + NeutralizeMut {} 178 | 179 | unsafe impl<'a, T> NeutralizeMut for Ref<'a, T> where T: ?Sized + Neutralize {} 180 | unsafe impl<'a, T> Neutralize for Ref<'a, T> where T: ?Sized + Neutralize {} 181 | 182 | unsafe impl<'a, T> NeutralizeMut for RefMut<'a, T> where T: ?Sized + NeutralizeMut {} 183 | unsafe impl<'a, T> Neutralize for RefMut<'a, T> where T: ?Sized + Neutralize {} 184 | 185 | #[cfg(feature = "std")] 186 | unsafe impl NeutralizeMut for Rc where T: ?Sized + Neutralize {} 187 | #[cfg(feature = "std")] 188 | unsafe impl Neutralize for Rc where T: ?Sized + Neutralize {} 189 | 190 | #[cfg(feature = "std")] 191 | unsafe impl<'a, T> NeutralizeMut for Cow<'a, T> 192 | where 193 | T: 'a + ?Sized + Neutralize + ToOwned, 194 | T::Owned: NeutralizeMut, 195 | { 196 | } 197 | 198 | #[cfg(feature = "std")] 199 | unsafe impl<'a, T> Neutralize for Cow<'a, T> 200 | where 201 | T: 'a + ?Sized + Neutralize + ToOwned, 202 | T::Owned: Neutralize, 203 | { 204 | } 205 | 206 | // There follow all the `NeutralizeUnsafe` impls for the types that we 207 | // mentioned in the previous important section. 208 | 209 | unsafe impl<'a, T> NeutralizeUnsafe for &'a T 210 | where 211 | T: ?Sized + NeutralizeUnsafe, 212 | { 213 | type Output = T::Output; 214 | 215 | #[inline] 216 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 217 | T::neutralize_unsafe(self) 218 | } 219 | } 220 | 221 | unsafe impl<'a, T> NeutralizeUnsafe for &'a mut T 222 | where 223 | T: ?Sized + NeutralizeUnsafe, 224 | { 225 | type Output = T::Output; 226 | 227 | #[inline] 228 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 229 | T::neutralize_unsafe(self) 230 | } 231 | } 232 | 233 | unsafe impl NeutralizeUnsafe for Cell 234 | where 235 | T: ?Sized, 236 | { 237 | type Output = Inert; 238 | 239 | #[inline] 240 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 241 | Inert::new_unchecked(&*self.as_ptr()) 242 | } 243 | } 244 | 245 | unsafe impl NeutralizeUnsafe for RefCell 246 | where 247 | T: ?Sized, 248 | { 249 | type Output = InertRefCell; 250 | 251 | #[inline] 252 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 253 | &*(self as *const Self as *const Self::Output) 254 | } 255 | } 256 | 257 | /// An inert `RefCell`. 258 | pub struct InertRefCell { 259 | value: Neutralized>, 260 | } 261 | 262 | impl InertRefCell 263 | where 264 | T: ?Sized, 265 | { 266 | /// Returns an inert reference to the inner `T`. 267 | /// 268 | /// # Panics 269 | /// 270 | /// This method panics if the `RefCell` is currently borrowed mutably. 271 | pub fn borrow(&self) -> &Inert { 272 | // FIXME(nox): This is nuts but life is too short to wait for 273 | // RefCell::borrow_state to land. 274 | // 275 | // https://github.com/rust-lang/rust/pull/59211 276 | 277 | struct RefCellRepr { 278 | flag: Cell, 279 | _value: UnsafeCell, 280 | } 281 | 282 | #[cold] 283 | #[inline(never)] 284 | fn panic() { 285 | panic!("already mutably borrowed") 286 | } 287 | 288 | unsafe { 289 | if (*(self as *const Self as *const RefCellRepr)).flag.get() < 0 { 290 | panic(); 291 | } 292 | Inert::new_unchecked(&*self.value.as_ref().as_ptr()) 293 | } 294 | } 295 | } 296 | 297 | unsafe impl<'a, T> NeutralizeUnsafe for Ref<'a, T> 298 | where 299 | T: ?Sized, 300 | { 301 | type Output = Inert; 302 | 303 | #[inline] 304 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 305 | Inert::new_unchecked(self) 306 | } 307 | } 308 | 309 | unsafe impl<'a, T> NeutralizeUnsafe for RefMut<'a, T> 310 | where 311 | T: ?Sized, 312 | { 313 | type Output = Inert; 314 | 315 | #[inline] 316 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 317 | Inert::new_unchecked(self) 318 | } 319 | } 320 | 321 | #[cfg(feature = "std")] 322 | unsafe impl NeutralizeUnsafe for Rc 323 | where 324 | T: ?Sized, 325 | { 326 | type Output = Inert; 327 | 328 | #[inline] 329 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 330 | Inert::new_unchecked(self) 331 | } 332 | } 333 | 334 | #[cfg(feature = "std")] 335 | unsafe impl<'a, T> NeutralizeUnsafe for Cow<'a, T> 336 | where 337 | T: 'a + ?Sized + NeutralizeUnsafe + ToOwned, 338 | T::Owned: NeutralizeUnsafe, 339 | { 340 | type Output = T::Output; 341 | 342 | #[inline] 343 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 344 | match *self { 345 | Cow::Borrowed(ref this) => T::neutralize_unsafe(this), 346 | Cow::Owned(ref this) => { 347 | // We can't just deref the Cow because it calls 348 | // T::Owned::borrow and T::Owned may not be Sync. 349 | T::Owned::neutralize_unsafe(this) 350 | }, 351 | } 352 | } 353 | } 354 | 355 | // Inert implements many traits through T::Output. 356 | 357 | impl AsRef for Inert 358 | where 359 | T: ?Sized + NeutralizeUnsafe, 360 | { 361 | #[inline] 362 | fn as_ref(&self) -> &T::Output { 363 | self 364 | } 365 | } 366 | 367 | impl PartialEq for Inert 368 | where 369 | T: ?Sized + NeutralizeUnsafe, 370 | T::Output: PartialEq, 371 | { 372 | #[inline] 373 | fn eq(&self, other: &Self) -> bool { 374 | T::Output::eq(self, other) 375 | } 376 | } 377 | 378 | impl Eq for Inert 379 | where 380 | T: ?Sized + NeutralizeUnsafe, 381 | T::Output: Eq, 382 | { 383 | } 384 | 385 | impl PartialOrd for Inert 386 | where 387 | T: ?Sized + NeutralizeUnsafe, 388 | T::Output: PartialOrd, 389 | { 390 | #[inline] 391 | fn partial_cmp(&self, other: &Self) -> Option { 392 | T::Output::partial_cmp(self, other) 393 | } 394 | } 395 | 396 | impl Ord for Inert 397 | where 398 | T: ?Sized + NeutralizeUnsafe, 399 | T::Output: Ord, 400 | { 401 | #[inline] 402 | fn cmp(&self, other: &Self) -> Ordering { 403 | T::Output::cmp(self, other) 404 | } 405 | } 406 | 407 | impl fmt::Debug for Inert 408 | where 409 | T: ?Sized + NeutralizeUnsafe, 410 | T::Output: fmt::Debug, 411 | { 412 | #[inline] 413 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 414 | T::Output::fmt(self, fmt) 415 | } 416 | } 417 | 418 | impl fmt::Display for Inert 419 | where 420 | T: ?Sized + NeutralizeUnsafe, 421 | T::Output: fmt::Display, 422 | { 423 | #[inline] 424 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 425 | T::Output::fmt(self, fmt) 426 | } 427 | } 428 | 429 | // 🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖 430 | // Below this sandwich are only uninteresting and trivial implementations. 431 | // 🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖🥖 432 | 433 | unsafe impl NeutralizeUnsafe for [T] { 434 | type Output = [Inert]; 435 | 436 | #[inline] 437 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 438 | &*(self as *const Self as *const Self::Output) 439 | } 440 | } 441 | 442 | unsafe impl NeutralizeMut for [T] where T: NeutralizeMut {} 443 | unsafe impl Neutralize for [T] where T: Neutralize {} 444 | 445 | #[cfg(feature = "std")] 446 | unsafe impl NeutralizeUnsafe for Vec { 447 | // Not `Vec>` so that `Cow<[T]>` works. 448 | type Output = [Inert]; 449 | 450 | #[inline] 451 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 452 | <[T]>::neutralize_unsafe(self) 453 | } 454 | } 455 | 456 | #[cfg(feature = "std")] 457 | unsafe impl NeutralizeMut for Vec where T: NeutralizeMut {} 458 | #[cfg(feature = "std")] 459 | unsafe impl Neutralize for Vec where T: Neutralize {} 460 | 461 | macro_rules! neutralize_as_deref { 462 | ($($($id:ident)::* <$($param:tt),*>,)*) => {$( 463 | unsafe impl<$($param),*> NeutralizeUnsafe for $($id)::* <$($param),*> 464 | where 465 | $($param: ?Sized + NeutralizeUnsafe,)* 466 | { 467 | type Output = T::Output; 468 | 469 | #[inline] 470 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 471 | T::neutralize_unsafe(self) 472 | } 473 | } 474 | 475 | unsafe impl<$($param),*> NeutralizeMut for $($id)::* <$($param),*> 476 | where 477 | $($param: ?Sized + NeutralizeMut,)* 478 | { 479 | } 480 | 481 | unsafe impl<$($param),*> Neutralize for $($id)::* <$($param),*> 482 | where 483 | $($param: ?Sized + Neutralize,)* 484 | { 485 | } 486 | )*}; 487 | } 488 | 489 | neutralize_as_deref! { 490 | core::mem::ManuallyDrop, 491 | } 492 | 493 | #[cfg(feature = "std")] 494 | neutralize_as_deref! { 495 | Box, 496 | } 497 | 498 | #[cfg(feature = "std")] 499 | macro_rules! neutralize_as_target { 500 | ($($ty:ty => $output:ty,)*) => {$( 501 | unsafe impl NeutralizeUnsafe for $ty { 502 | type Output = $output; 503 | 504 | #[inline] 505 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 506 | <$output>::neutralize_unsafe(self) 507 | } 508 | } 509 | 510 | unsafe impl NeutralizeMut for $ty {} 511 | unsafe impl Neutralize for $ty {} 512 | )*}; 513 | } 514 | 515 | #[cfg(feature = "std")] 516 | neutralize_as_target! { 517 | String => str, 518 | std::ffi::CString => std::ffi::CStr, 519 | std::ffi::OsString => std::ffi::OsStr, 520 | std::path::PathBuf => std::path::Path, 521 | } 522 | 523 | macro_rules! neutralize_as_self { 524 | ($($($id:ident)::* $(<$($param:tt),*>)* $(($($p:ident: ($($bound:tt)*)),*))*,)*) => {$( 525 | unsafe impl $(<$($param),*>)* NeutralizeUnsafe for $($id)::* $(<$($param),*>)* 526 | $(where 527 | $($p: $($bound)*),*)* 528 | { 529 | type Output = Self; 530 | 531 | #[inline] 532 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 533 | self 534 | } 535 | } 536 | 537 | unsafe impl $(<$($param),*>)* NeutralizeMut for $($id)::* $(<$($param),*>)* 538 | $(where 539 | $($p: $($bound)*),*)* 540 | { 541 | } 542 | 543 | unsafe impl $(<$($param),*>)* Neutralize for $($id)::* $(<$($param),*>)* 544 | $(where 545 | $($p: $($bound)*),*)* 546 | { 547 | } 548 | )*}; 549 | } 550 | 551 | // Non-generic primitives. 552 | neutralize_as_self! { 553 | bool, 554 | char, str, 555 | f32, f64, 556 | i8, i16, i32, i64, i128, isize, 557 | u8, u16, u32, u64, u128, usize, 558 | } 559 | 560 | // Sync types from libcore. 561 | neutralize_as_self! { 562 | core::alloc::Layout, 563 | core::alloc::LayoutErr, 564 | core::any::TypeId, 565 | core::ascii::EscapeDefault, 566 | core::cell::BorrowError, 567 | core::cell::BorrowMutError, 568 | core::char::DecodeUtf16Error, 569 | core::char::EscapeDebug, 570 | core::char::EscapeDefault, 571 | core::char::EscapeUnicode, 572 | core::char::ParseCharError, 573 | core::char::ToLowercase, 574 | core::char::ToUppercase, 575 | core::cmp::Ordering, 576 | core::fmt::Alignment, 577 | core::fmt::Error, 578 | core::mem::Discriminant, 579 | core::num::FpCategory, 580 | core::num::NonZeroU8, 581 | core::num::NonZeroU16, 582 | core::num::NonZeroU32, 583 | core::num::NonZeroU64, 584 | core::num::NonZeroU128, 585 | core::num::NonZeroUsize, 586 | core::num::ParseFloatError, 587 | core::num::ParseIntError, 588 | core::ops::RangeFull, 589 | core::str::CharIndices<'a>, 590 | core::str::Chars<'a>, 591 | core::str::Utf8Error, 592 | core::sync::atomic::AtomicBool, 593 | core::sync::atomic::AtomicIsize, 594 | core::sync::atomic::AtomicPtr, 595 | core::sync::atomic::AtomicUsize, 596 | core::sync::atomic::Ordering, 597 | core::time::Duration, 598 | } 599 | 600 | // SIMD types (x86). 601 | #[cfg(target_arch = "x86")] 602 | neutralize_as_self! { 603 | core::arch::x86::CpuidResult, 604 | core::arch::x86::__m128, 605 | core::arch::x86::__m128d, 606 | core::arch::x86::__m128i, 607 | core::arch::x86::__m256, 608 | core::arch::x86::__m256d, 609 | core::arch::x86::__m256i, 610 | } 611 | 612 | // SIMD types (x86_64). 613 | #[cfg(target_arch = "x86_64")] 614 | neutralize_as_self! { 615 | core::arch::x86_64::CpuidResult, 616 | core::arch::x86_64::__m128, 617 | core::arch::x86_64::__m128d, 618 | core::arch::x86_64::__m128i, 619 | core::arch::x86_64::__m256, 620 | core::arch::x86_64::__m256d, 621 | core::arch::x86_64::__m256i, 622 | } 623 | 624 | // Sync types from libstd. 625 | #[cfg(feature = "std")] 626 | neutralize_as_self! { 627 | std::alloc::System, 628 | std::collections::hash_map::DefaultHasher, 629 | std::collections::hash_map::RandomState, 630 | std::env::VarError, 631 | std::ffi::CStr, 632 | std::ffi::FromBytesWithNulError, 633 | std::ffi::IntoStringError, 634 | std::ffi::NulError, 635 | std::ffi::OsStr, 636 | std::fs::DirBuilder, 637 | std::fs::DirEntry, 638 | std::fs::File, 639 | std::fs::FileType, 640 | std::fs::Metadata, 641 | std::fs::OpenOptions, 642 | std::fs::Permissions, 643 | std::io::Empty, 644 | std::io::Error, 645 | std::io::ErrorKind, 646 | std::io::Repeat, 647 | std::io::SeekFrom, 648 | std::io::Sink, 649 | std::io::Stderr, 650 | std::io::StderrLock<'a>, 651 | std::io::Stdin, 652 | std::io::StdinLock<'a>, 653 | std::io::Stdout, 654 | std::io::StdoutLock<'a>, 655 | std::net::AddrParseError, 656 | std::net::Incoming<'a>, 657 | std::net::IpAddr, 658 | std::net::Ipv4Addr, 659 | std::net::Ipv6Addr, 660 | std::net::Shutdown, 661 | std::net::SocketAddr, 662 | std::net::SocketAddrV4, 663 | std::net::SocketAddrV6, 664 | std::net::TcpListener, 665 | std::net::TcpStream, 666 | std::net::UdpSocket, 667 | std::panic::Location<'a>, 668 | std::path::Ancestors<'a>, 669 | std::path::Components<'a>, 670 | std::path::Display<'a>, 671 | std::path::Iter<'a>, 672 | std::path::Path, 673 | std::path::PrefixComponent<'a>, 674 | std::path::StripPrefixError, 675 | std::process::Child, 676 | std::process::ChildStderr, 677 | std::process::ChildStdin, 678 | std::process::ChildStdout, 679 | std::process::ExitStatus, 680 | std::process::Output, 681 | std::process::Stdio, 682 | std::sync::Arc (T: (?Sized + Send + Sync)), 683 | std::sync::Barrier, 684 | std::sync::BarrierWaitResult, 685 | std::sync::Condvar, 686 | std::sync::Mutex (T: (?Sized + Send)), 687 | std::sync::MutexGuard<'a, T> (T: (?Sized + Sync)), 688 | std::sync::Once, 689 | std::sync::PoisonError (T: (Sync)), 690 | std::sync::RwLockReadGuard<'a, T> (T: (?Sized + Sync)), 691 | std::sync::RwLockWriteGuard<'a, T> (T: (?Sized + Sync)), 692 | std::sync::TryLockError (T: (Sync)), 693 | std::sync::WaitTimeoutResult, 694 | std::sync::Weak (T: (?Sized + Send + Sync)), 695 | std::sync::mpsc::RecvError, 696 | std::sync::mpsc::RecvTimeoutError, 697 | std::sync::mpsc::SendError (T: (Sync)), 698 | std::sync::mpsc::SyncSender (T: (Send)), 699 | std::sync::mpsc::TryRecvError, 700 | std::sync::mpsc::TrySendError (T: (Sync)), 701 | std::thread::AccessError, 702 | std::thread::Builder, 703 | std::thread::JoinHandle, 704 | std::thread::LocalKey, 705 | std::thread::Thread, 706 | std::thread::ThreadId, 707 | std::time::Instant, 708 | std::time::SystemTime, 709 | std::time::SystemTimeError, 710 | } 711 | 712 | macro_rules! neutralize_array { 713 | ($($len:expr,)*) => {$( 714 | unsafe impl NeutralizeUnsafe for [T; $len] 715 | where 716 | T: NeutralizeUnsafe, 717 | { 718 | type Output = [Inert; $len]; 719 | 720 | #[inline] 721 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 722 | &*(self as *const Self as *const Self::Output) 723 | } 724 | } 725 | )*} 726 | } 727 | 728 | neutralize_array! { 729 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 730 | 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 731 | } 732 | 733 | macro_rules! neutralize_tuple { 734 | ($(($($p:ident),*),)*) => {$( 735 | unsafe impl<$($p),*> NeutralizeUnsafe for ($($p,)*) { 736 | type Output = ($(Inert<$p>),*); 737 | 738 | #[inline] 739 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 740 | &*(self as *const Self as *const Self::Output) 741 | } 742 | } 743 | 744 | unsafe impl<$($p),*> NeutralizeMut for ($($p,)*) 745 | where 746 | $($p: NeutralizeMut,)* 747 | { 748 | } 749 | 750 | unsafe impl<$($p),*> Neutralize for ($($p,)*) 751 | where 752 | $($p: Neutralize,)* 753 | { 754 | } 755 | )*} 756 | } 757 | 758 | neutralize_tuple! { 759 | (), 760 | (A), 761 | (A, B), 762 | (A, B, C), 763 | (A, B, C, D), 764 | (A, B, C, D, E), 765 | (A, B, C, D, E, F), 766 | (A, B, C, D, E, F, G), 767 | (A, B, C, D, E, F, G, H), 768 | (A, B, C, D, E, F, G, H, I), 769 | (A, B, C, D, E, F, G, H, I, J), 770 | (A, B, C, D, E, F, G, H, I, J, K), 771 | (A, B, C, D, E, F, G, H, I, J, K, L), 772 | } 773 | 774 | macro_rules! neutralize_as_ptr_cast { 775 | ($($($id:ident)::* <$($lt:lifetime,)* $($param:ident),*>,)*) => {$( 776 | unsafe impl<$($lt,)* $($param),*> NeutralizeUnsafe for $($id)::* <$($lt,)* $($param),*> { 777 | type Output = $($id)::* <$($lt,)* $(Inert<$param>),*>; 778 | 779 | #[inline] 780 | unsafe fn neutralize_unsafe(&self) -> &Self::Output { 781 | &*(self as *const Self as *const Self::Output) 782 | } 783 | } 784 | 785 | unsafe impl<$($lt,)* $($param),*> NeutralizeMut for $($id)::* <$($lt,)* $($param),*> 786 | where 787 | $($param: NeutralizeMut,)* 788 | { 789 | } 790 | 791 | unsafe impl<$($lt,)* $($param),*> Neutralize for $($id)::* <$($lt,)* $($param),*> 792 | where 793 | $($param: Neutralize,)* 794 | { 795 | } 796 | )*}; 797 | } 798 | 799 | neutralize_as_ptr_cast! { 800 | Option, 801 | Result, 802 | core::cmp::Reverse, 803 | core::hash::BuildHasherDefault, 804 | core::ops::Bound, 805 | core::ops::Range, 806 | core::ops::RangeFrom, 807 | core::ops::RangeInclusive, 808 | core::ops::RangeTo, 809 | core::ops::RangeToInclusive, 810 | core::num::Wrapping, 811 | core::slice::Chunks<'a, T>, 812 | core::slice::ChunksExact<'a, T>, 813 | core::slice::Iter<'a, T>, 814 | core::slice::RChunks<'a, T>, 815 | core::slice::RChunksExact<'a, T>, 816 | } 817 | 818 | #[cfg(feature = "std")] 819 | neutralize_as_ptr_cast! { 820 | std::collections::binary_heap::BinaryHeap, 821 | std::collections::binary_heap::Iter<'a, T>, 822 | std::collections::btree_map::BTreeMap, 823 | std::collections::btree_map::Entry<'a, K, V>, 824 | std::collections::btree_map::Iter<'a, K, V>, 825 | std::collections::btree_map::Keys<'a, K, V>, 826 | std::collections::btree_map::OccupiedEntry<'a, K, V>, 827 | std::collections::btree_map::Range<'a, K, V>, 828 | std::collections::btree_map::VacantEntry<'a, K, V>, 829 | std::collections::btree_set::BTreeSet, 830 | std::collections::btree_set::Iter<'a, T>, 831 | std::collections::btree_set::Range<'a, T>, 832 | std::collections::hash_map::HashMap, 833 | std::collections::hash_map::Iter<'a, K, V>, 834 | std::collections::hash_map::Keys<'a, K, V>, 835 | std::collections::hash_map::Values<'a, K, V>, 836 | std::collections::hash_set::HashSet, 837 | std::collections::hash_set::Iter<'a, T>, 838 | std::collections::linked_list::Iter<'a, T>, 839 | std::collections::linked_list::LinkedList, 840 | std::collections::vec_deque::Iter<'a, T>, 841 | std::collections::vec_deque::VecDeque, 842 | std::io::BufReader, 843 | std::io::Chain, 844 | std::io::Cursor, 845 | std::io::IntoInnerError, 846 | std::io::Take, 847 | std::iter::Chain, 848 | std::iter::Cycle, 849 | std::iter::Empty, 850 | std::iter::Enumerate, 851 | std::iter::Fuse, 852 | std::iter::Once, 853 | std::iter::Rev, 854 | std::iter::Skip, 855 | std::iter::StepBy, 856 | std::iter::Take, 857 | std::iter::Zip, 858 | std::panic::AssertUnwindSafe, 859 | } 860 | 861 | // TODO(nox): https://github.com/rust-lang/rust/pull/58369 862 | // 863 | // * std::collections::hash_map::Entry 864 | // * std::collections::hash_map::OccupiedEntry 865 | // * std::collections::hash_map::VacantEntry 866 | -------------------------------------------------------------------------------- /tests/refcells.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | 3 | use std::cell::RefCell; 4 | use inert::{Inert, NeutralizeMut}; 5 | 6 | #[test] 7 | fn inert_new_mut() { 8 | let mut tree = tree(); 9 | let inert = Inert::new_mut(&mut tree); 10 | 11 | assert_eq!(inert.borrow().name().0, "premature optimisation"); 12 | 13 | let wrath = &inert.borrow().children()[4]; 14 | assert_eq!(wrath.borrow().name().0, "wrath"); 15 | 16 | let me = &wrath.borrow().children()[0]; 17 | assert_eq!(me.borrow().name().0, "nox"); 18 | 19 | let best_language_ever = &inert.borrow().children()[0].borrow().children()[0]; 20 | assert_eq!(best_language_ever.borrow().name().0, "coq"); 21 | } 22 | 23 | #[inert::neutralize(as unsafe InertNode)] 24 | struct Node { 25 | #[inert::field] 26 | name: Name, 27 | #[inert::field] 28 | children: Vec>, 29 | } 30 | 31 | #[inert::neutralize(as Self)] 32 | struct Name(String); 33 | 34 | impl Node { 35 | fn new(name: &str) -> RefCell { 36 | RefCell::new(Self { name: Name(name.into()), children: vec![] }) 37 | } 38 | 39 | fn append_child(&mut self, child: RefCell) { 40 | self.children.push(child); 41 | } 42 | } 43 | 44 | fn tree() -> RefCell { 45 | let mut root = Node::new("premature optimisation"); 46 | 47 | let mut lust = Node::new("lust"); 48 | lust.get_mut().append_child(Node::new("coq")); 49 | root.get_mut().append_child(lust); 50 | 51 | let mut gluttony = Node::new("gluttony"); 52 | gluttony.get_mut().append_child(Node::new("chrome")); 53 | gluttony.get_mut().append_child(Node::new("slack")); 54 | gluttony.get_mut().append_child(Node::new("atom")); 55 | root.get_mut().append_child(gluttony); 56 | 57 | let mut greed = Node::new("greed"); 58 | greed.get_mut().append_child(Node::new("airbnb")); 59 | greed.get_mut().append_child(Node::new("amazon")); 60 | greed.get_mut().append_child(Node::new("facebook")); 61 | greed.get_mut().append_child(Node::new("google")); 62 | greed.get_mut().append_child(Node::new("uber")); 63 | root.get_mut().append_child(greed); 64 | 65 | let mut sloth = Node::new("sloth"); 66 | sloth.get_mut().append_child(Node::new("haskell")); 67 | root.get_mut().append_child(sloth); 68 | 69 | let mut wrath = Node::new("wrath"); 70 | wrath.get_mut().append_child(Node::new("nox")); 71 | root.get_mut().append_child(wrath); 72 | 73 | let mut envy = Node::new("envy"); 74 | envy.get_mut().append_child(Node::new("javascript")); 75 | root.get_mut().append_child(envy); 76 | 77 | let mut pride = Node::new("pride"); 78 | pride.get_mut().append_child(Node::new("c")); 79 | pride.get_mut().append_child(Node::new("c++")); 80 | root.get_mut().append_child(pride); 81 | 82 | root 83 | } 84 | 85 | // FIXME(nox): We should be able to derive that impl, so that the getter 86 | // can type check that their return type does indeed implement `NeutralizeMut` 87 | // themselves. 88 | #[allow(unsafe_code)] 89 | unsafe impl NeutralizeMut for Node {} 90 | --------------------------------------------------------------------------------