├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples └── simple.rs └── src ├── lib.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | jobs: 5 | fast_finish: true 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "real-async-trait" 3 | version = "0.0.2" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2018" 6 | description = "A proc macro that uses experimental features to work around type erasure for async traits" 7 | readme = "README.md" 8 | repository = "https://github.com/4lDO2/real-async-trait-rs" 9 | license = "MIT OR Apache-2.0" 10 | keywords = ["async-trait", "gats", "existential-types", "proc-macro", "async"] 11 | categories = ["asynchronous"] 12 | 13 | [badges] 14 | travis-ci = { repository = "4lDO2/real-async-trait-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "4lDO2/real-async-trait-rs" } 16 | is-it-maintained-open-issues = { repository = "4lDO2/real-async-trait-rs" } 17 | 18 | [lib] 19 | proc-macro = true 20 | 21 | [dependencies] 22 | proc-macro2 = "1" 23 | quote = "1" 24 | syn = { version = "1", features = ["full", "visit"] } 25 | 26 | [dev-dependencies] 27 | async-std = { version = "1.6", features = ["attributes"] } 28 | syn = { version = "1", features = ["full", "visit", "extra-traits"] } 29 | -------------------------------------------------------------------------------- /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 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 4lDO2 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `#[real_async_trait]` 2 | [![Build Status](https://travis-ci.org/4lDO2/real-async-trait-rs.svg?branch=master)](https://travis-ci.org/4lDO2/real-async-trait-rs) 3 | [![Crates.io](https://img.shields.io/crates/v/real-async-trait.svg)](https://crates.io/crates/real-async-trait) 4 | [![Documentation](https://docs.rs/real-async-trait/badge.svg)](https://docs.rs/real-async-trait/) 5 | 6 | This nightly-only crate provides a proof-of-concept proc macro attribute that 7 | allows async functions within traits, without the possible runtime overhead of 8 | wrapping everything in a `Box` and erasing the types. This is made possible 9 | thanks to the unstable `generic_associated_types` and `type_alias_impl_trait` 10 | nightly features. 11 | 12 | ## License 13 | 14 | Licensed under either of 15 | 16 | * Apache License, Version 2.0 17 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 18 | * MIT license 19 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 20 | 21 | at your option. 22 | 23 | ## Contribution 24 | 25 | Unless you explicitly state otherwise, any contribution intentionally submitted 26 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 27 | dual licensed as above, without any additional terms or conditions. 28 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | #![feature(generic_associated_types, type_alias_impl_trait)] 2 | extern crate real_async_trait; 3 | 4 | use std::collections::BTreeMap; 5 | use std::str; 6 | 7 | use real_async_trait::real_async_trait; 8 | 9 | pub type Errno = usize; 10 | pub const ENOENT: usize = 1; 11 | pub const EBADF: usize = 2; 12 | 13 | #[real_async_trait] 14 | pub trait AsyncScheme { 15 | #[send] 16 | async fn open<'a>(&'a mut self, path: &'a [u8], flags: usize) -> Result; 17 | async fn close<'a>(&'a mut self, fd: usize) -> Result<(), Errno>; 18 | async fn read<'a>(&'a mut self, fd: usize, num: &'a mut u64) -> Result<(), Errno>; 19 | async fn write<'a>(&'a mut self, fd: usize, num: &'a u64) -> Result<(), Errno>; 20 | } 21 | 22 | struct NumberScheme { 23 | handles: BTreeMap, 24 | } 25 | 26 | #[real_async_trait] 27 | impl AsyncScheme for NumberScheme { 28 | #[send] 29 | async fn open<'a>(&'a mut self, path: &'a [u8], _flags: usize) -> Result { 30 | let path_str = str::from_utf8(path).or(Err(ENOENT))?; 31 | let num = path_str.parse::().or(Err(ENOENT))?; 32 | self.handles.insert(num, 0); 33 | Ok(num) 34 | } 35 | async fn close<'a>(&'a mut self, fd: usize) -> Result<(), Errno> { 36 | if self.handles.remove(&fd).is_none() { 37 | return Err(EBADF); 38 | } 39 | Ok(()) 40 | } 41 | async fn read<'a>(&'a mut self, fd: usize, num: &'a mut u64) -> Result<(), Errno> { 42 | let handle = self.handles.get(&fd).ok_or(ENOENT)?; 43 | *num = *handle; 44 | Ok(()) 45 | } 46 | async fn write<'a>(&'a mut self, fd: usize, num: &'a u64) -> Result<(), Errno> { 47 | let handle = self.handles.get_mut(&fd).ok_or(ENOENT)?; 48 | *handle = *num; 49 | Ok(()) 50 | } 51 | } 52 | 53 | #[async_std::main] 54 | async fn main() -> Result<(), Errno> { 55 | let mut numberscheme = NumberScheme { 56 | handles: BTreeMap::new(), 57 | }; 58 | 59 | let mut number_buf = 0u64; 60 | let input_buf = 420u64; 61 | 62 | println!("Opening valuable number container `1337`"); 63 | let fd = numberscheme.open(b"1337", 1).await?; 64 | println!("Opened number container `1337`"); 65 | 66 | println!( 67 | "Initiating read from number container `1337` (fd {}), into buffer at {:p}", 68 | fd, &number_buf as *const _ 69 | ); 70 | numberscheme.read(fd, &mut number_buf).await?; 71 | println!( 72 | "Completed read from number container `1337` (fd {}), new value: {}", 73 | fd, number_buf 74 | ); 75 | 76 | println!( 77 | "Initiating write into number container `1337` (fd {}), from buffer at {:p}", 78 | fd, &input_buf as *const _ 79 | ); 80 | numberscheme.write(fd, &input_buf).await?; 81 | println!( 82 | "Completed write into number container `1337` (fd {}), new value: {}", 83 | fd, input_buf 84 | ); 85 | 86 | println!( 87 | "Initiating second read from number container `1337` (fd {}), into buffer at {:p}", 88 | fd, &number_buf as *const _ 89 | ); 90 | numberscheme.read(fd, &mut number_buf).await?; 91 | println!( 92 | "Completed second read from number container `1337` (fd {}), new value: {}", 93 | fd, number_buf 94 | ); 95 | 96 | println!("Closing number container `1337` (fd {})", fd); 97 | numberscheme.close(fd).await?; 98 | println!("Number container `1337` (fd {}) closed", fd); 99 | 100 | Ok(()) 101 | } 102 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # `#[real_async_trait]` 3 | //! [![travis]](https://travis-ci.org/4lDO2/real-async-trait-rs) 4 | //! [![cratesio]](https://crates.io/crates/real-async-trait) 5 | //! [![docsrs]](https://docs.rs/real-async-trait/) 6 | //! 7 | //! [travis]: https://travis-ci.org/4lDO2/real-async-trait-rs.svg?branch=master 8 | //! [cratesio]: https://img.shields.io/crates/v/real-async-trait.svg 9 | //! [docsrs]: https://docs.rs/real-async-trait/badge.svg 10 | //! 11 | //! This crate provides a producedural macro that works around the current limitation of not being 12 | //! able to put `async fn`s in a trait, _without type erasure_, by using experimental 13 | //! nightly-features, namely [generic associated types 14 | //! (GATs)](https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md) 15 | //! and [existential 16 | //! types](https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md). 17 | //! 18 | //! ## Caveats 19 | //! 20 | //! While this proc macro will allow you to write non-type-erased allocation-free async fns within 21 | //! traits, there are a few caveats to this (non-exhaustive): 22 | //! 23 | //! * at the moment, all references used in the async fn, must have their lifetimes be explicitly 24 | //! specified, either from the top-level of the trait, or in the function declaration; 25 | //! * there can only be a single lifetime in use simultaneously. It appears to be caused by 26 | //! [#61756](https://github.com/rust-lang/rust/issues/61756), but could perhaps be worked around 27 | //! using a `Capture` trait; 28 | //! * While this proc macro allows traits to also require a `Send` trait bound on futures, it is 29 | //! not particularly trivial if at all possible, to require that the future implement `Send`, if 30 | //! and only if `Self: Send`. It _may_ be possible using some HRTB hack, but would require a 31 | //! language feature in the long term. 32 | //! * since GATs are an "incomplete" feature in rust, it may not be sound or just not compile 33 | //! correctly or at all. __Don't use this in production code!__ 34 | //! 35 | //! ## Example 36 | //! ``` 37 | //! #![feature(generic_associated_types, type_alias_impl_trait)] 38 | //! use real_async_trait::real_async_trait; 39 | //! 40 | //! /// An error code, similar to `errno` in C. 41 | //! pub type Errno = usize; 42 | //! 43 | //! /// A UNIX-like file descriptor. 44 | //! pub type FileDescriptor = usize; 45 | //! 46 | //! /// "No such file or directory" 47 | //! pub const ENOENT: usize = 1; 48 | //! 49 | //! /// "Bad file descriptor" 50 | //! pub const EBADF: usize = 2; 51 | //! 52 | //! /// A filesystem-like primitive, used in the Redox Operating System. 53 | //! #[real_async_trait] 54 | //! pub trait RedoxScheme { 55 | //! /// Some async fns can force the implementor to also implement `Send` for the future. 56 | //! #[send] 57 | //! async fn open<'a>(&'a self, path: &'a [u8], flags: usize) -> Result; 58 | //! async fn read<'a>(&'a self, fd: FileDescriptor, buffer: &'a mut [u8]) -> Result; 59 | //! async fn write<'a>(&'a self, fd: FileDescriptor, buffer: &'a [u8]) -> Result; 60 | //! async fn close<'a>(&'a self, fd: FileDescriptor) -> Result<(), Errno>; 61 | //! } 62 | //! 63 | //! /// A scheme that does absolutely nothing. 64 | //! struct MyNothingScheme; 65 | //! 66 | //! #[real_async_trait] 67 | //! impl RedoxScheme for MyNothingScheme { 68 | //! #[send] 69 | //! async fn open<'a>(&'a self, path: &'a [u8], flags: usize) -> Result { 70 | //! // I can write async code in here! 71 | //! Err(ENOENT) 72 | //! } 73 | //! async fn read<'a>(&'a self, fd: FileDescriptor, buffer: &'a mut [u8]) -> Result { 74 | //! Err(EBADF) 75 | //! } 76 | //! async fn write<'a>(&'a self, fd: FileDescriptor, path: &'a [u8]) -> Result { 77 | //! Err(EBADF) 78 | //! } 79 | //! async fn close<'a>(&'a self, fd: FileDescriptor) -> Result<(), Errno> { 80 | //! Err(EBADF) 81 | //! } 82 | //! } 83 | //! 84 | //! # #[async_std::main] 85 | //! # async fn main() { 86 | //! 87 | //! let my_nothing_scheme = MyNothingScheme; 88 | //! 89 | //! assert_eq!(my_nothing_scheme.open(b"nothing exists here", 0).await, Err(ENOENT), "why would anything exist here?"); 90 | //! assert_eq!(my_nothing_scheme.read(1337, &mut []).await, Err(EBADF)); 91 | //! assert_eq!(my_nothing_scheme.write(1337, &[]).await, Err(EBADF)); 92 | //! assert_eq!(my_nothing_scheme.close(1337).await, Err(EBADF)); 93 | //! 94 | //! # } 95 | //! 96 | //! ``` 97 | //! ## How it works 98 | //! 99 | //! Under the hood, this proc macro will insert generic associated types (GATs) for the the futures 100 | //! that are the return types of the async fns in the trait definition. The macro will generate the 101 | //! following for the `RedoxScheme` trait (simplified generated names): 102 | //! 103 | //! ``` 104 | //! # #![feature(generic_associated_types)] 105 | //! # struct FileDescriptor; 106 | //! # struct Errno; 107 | //! pub trait RedoxScheme { 108 | //! // Downgraded functions, from async fn to fn. Their types have changed into a generic 109 | //! // associated type. 110 | //! fn open<'a>(&'a self, path: &'a [u8], flags: usize) -> Self::OpenFuture<'a>; 111 | //! fn read<'a>(&'a self, fd: FileDescriptor, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; 112 | //! fn write<'a>(&'a self, fd: FileDescriptor, buf: &'a [u8]) -> Self::WriteFuture<'a>; 113 | //! fn close<'a>(&'a self, fd: FileDescriptor) -> Self::CloseFuture<'a>; 114 | //! 115 | //! // Generic associated types, the return values are moved to here. 116 | //! // NOTE: all #[send] methods also get the `Send` trait bound. 117 | //! type OpenFuture<'a>: ::core::future::Future> + 118 | //! ::core::marker::Send + 'a where Self: 'a; 119 | //! type ReadFuture<'a>: ::core::future::Future> + 'a where Self: 'a; 120 | //! type WriteFuture<'a>: ::core::future::Future> + 'a where Self: 'a; 121 | //! type CloseFuture<'a>: ::core::future::Future> + 'a where Self: 'a; 122 | //! } 123 | //! ``` 124 | //! 125 | //! Meanwhile, the impls will get the following generated code (simplified here as well): 126 | //! 127 | //! ``` 128 | //! # #![feature(generic_associated_types, type_alias_impl_trait)] 129 | //! # struct FileDescriptor; 130 | //! # struct Errno; 131 | //! # pub trait RedoxScheme { 132 | //! # fn open<'a>(&'a self, path: &'a [u8], flags: usize) -> Self::OpenFuture<'a>; 133 | //! # fn read<'a>(&'a self, fd: FileDescriptor, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; 134 | //! # fn write<'a>(&'a self, fd: FileDescriptor, buf: &'a [u8]) -> Self::WriteFuture<'a>; 135 | //! # fn close<'a>(&'a self, fd: FileDescriptor) -> Self::CloseFuture<'a>; 136 | //! # type OpenFuture<'a>: ::core::future::Future> + Send + 'a where Self: 'a; 137 | //! # type ReadFuture<'a>: ::core::future::Future> + 'a where Self: 'a; 138 | //! # type WriteFuture<'a>: ::core::future::Future> + 'a where Self: 'a; 139 | //! # type CloseFuture<'a>: ::core::future::Future> + 'a where Self: 'a; 140 | //! # } 141 | //! # const ENOENT: Errno = Errno; 142 | //! # const EBADF: Errno = Errno; 143 | //! 144 | //! // Wrap everything in a private module to prevent the existential types from leaking. 145 | //! // XXX: Apparently rust doctests don't work that great with private modules :( 146 | //! // mod __private { 147 | //! # struct MyNothingScheme; 148 | //! impl RedoxScheme for MyNothingScheme { 149 | //! // Async fns are downgraded here as well, and the same thing goes with the return 150 | //! // values. 151 | //! fn open<'a>(&'a self, path: &'a [u8], flags: usize) -> Self::OpenFuture<'a> { 152 | //! // All expressions in async fns are wrapped in async blocks. The compiler will 153 | //! // automagically figure out the actual types of the existential type aliases, even 154 | //! // though they are anonymous. 155 | //! async move { Err(ENOENT) } 156 | //! } 157 | //! fn read<'a>(&'a self, fd: FileDescriptor, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { 158 | //! async move { Err(EBADF) } 159 | //! } 160 | //! fn write<'a>(&'a self, fd: FileDescriptor, buf: &'a [u8]) -> Self::WriteFuture<'a> { 161 | //! async move { Err(EBADF) } 162 | //! } 163 | //! fn close<'a>(&'a self, fd: FileDescriptor) -> Self::CloseFuture<'a> { 164 | //! async move { Err(EBADF) } 165 | //! } 166 | //! 167 | //! // This is the part where the existential types come in. Currently, there is no possible 168 | //! // way to use types within type aliases inside of trait implementations, that aren't 169 | //! // publicly accessible. As we need some way to assign a user-visible type to the async 170 | //! // blocks, and we don't want the user to have to box futures, existential types are the 171 | //! // solution. use existential types. 172 | //! type OpenFuture<'a> = OpenFutureExistentialType<'a>; 173 | //! type ReadFuture<'a> = ReadFutureExistentialType<'a>; 174 | //! type WriteFuture<'a> = WriteFutureExistentialType<'a>; 175 | //! type CloseFuture<'a> = CloseFutureExistentialType<'a>; 176 | //! } 177 | //! // This is where the return values actually are defined. At the moment these type alises 178 | //! // with impl trait can only occur outside of the trait itself, unfortunately. There can 179 | //! // only be one type that this type alias refers to, which the compiler will keep track of. 180 | //! type OpenFutureExistentialType<'a> = impl ::core::future::Future> + 181 | //! ::core::marker::Send + 'a; 182 | //! type ReadFutureExistentialType<'a> = impl ::core::future::Future> + 'a; 183 | //! type WriteFutureExistentialType<'a> = impl ::core::future::Future> + 'a; 184 | //! type CloseFutureExistentialType<'a> = impl ::core::future::Future> + 'a; 185 | //! // } 186 | //! ``` 187 | //! 188 | 189 | extern crate proc_macro; 190 | 191 | use std::iter::FromIterator; 192 | use std::str::FromStr; 193 | use std::{iter, mem}; 194 | 195 | use proc_macro2::{Span, TokenStream}; 196 | use quote::quote; 197 | use syn::punctuated::Punctuated; 198 | use syn::token::Where; 199 | use syn::{token, PredicateType, WhereClause, WherePredicate}; 200 | use syn::{ 201 | AngleBracketedGenericArguments, AttrStyle, Attribute, Binding, Block, Expr, ExprAsync, FnArg, 202 | GenericArgument, GenericParam, Generics, Ident, ImplItem, ImplItemType, ItemImpl, ItemTrait, 203 | ItemType, Lifetime, LifetimeDef, PatType, Path, PathArguments, PathSegment, ReturnType, 204 | Signature, Stmt, Token, TraitBound, TraitBoundModifier, TraitItem, TraitItemType, Type, 205 | TypeImplTrait, TypeParamBound, TypePath, TypeReference, TypeTuple, Visibility, 206 | }; 207 | 208 | mod tests; 209 | 210 | struct LifetimeVisitor; 211 | 212 | impl<'ast> syn::visit::Visit<'ast> for LifetimeVisitor { 213 | fn visit_type_reference(&mut self, i: &'ast TypeReference) { 214 | if i.lifetime.is_none() { 215 | panic!("Reference at {:?} lacked an explicit lifetime, which is required by this proc macro", i.and_token.span); 216 | } 217 | } 218 | } 219 | 220 | fn handle_item_impl(mut item: ItemImpl) -> TokenStream { 221 | let mut existential_type_defs = Vec::new(); 222 | let mut gat_defs = Vec::new(); 223 | 224 | for method in item 225 | .items 226 | .iter_mut() 227 | .filter_map(|item| { 228 | if let ImplItem::Method(method) = item { 229 | Some(method) 230 | } else { 231 | None 232 | } 233 | }) 234 | .filter(|method| method.sig.asyncness.is_some()) 235 | { 236 | method.sig.asyncness = None; 237 | 238 | validate_that_function_always_has_lifetimes(&method.sig); 239 | 240 | let (toplevel_lifetimes, function_lifetimes) = 241 | already_defined_lifetimes(&item.generics, &method.sig.generics); 242 | 243 | let existential_type_name = format!( 244 | "__real_async_trait_impl_ExistentialTypeFor_{}", 245 | method.sig.ident 246 | ); 247 | let existential_type_ident = Ident::new(&existential_type_name, Span::call_site()); 248 | 249 | let requires_send = method_requires_send(&mut method.attrs); 250 | 251 | existential_type_defs.push(ItemType { 252 | attrs: Vec::new(), 253 | eq_token: Token!(=)(Span::call_site()), 254 | generics: Generics { 255 | gt_token: Some(Token!(>)(Span::call_site())), 256 | lt_token: Some(Token!(<)(Span::call_site())), 257 | params: toplevel_lifetimes 258 | .iter() 259 | .cloned() 260 | .map(GenericParam::Lifetime) 261 | .collect(), 262 | where_clause: None, 263 | }, 264 | ident: existential_type_ident, 265 | semi_token: Token!(;)(Span::call_site()), 266 | vis: Visibility::Inherited, 267 | ty: Box::new(Type::ImplTrait(TypeImplTrait { 268 | bounds: iter::once(TypeParamBound::Trait(future_trait_bound(return_type( 269 | method.sig.output.clone(), 270 | )))) 271 | .chain(if requires_send { 272 | Some(TypeParamBound::Trait(send_trait_bound())) 273 | } else { 274 | None 275 | }) 276 | .chain( 277 | toplevel_lifetimes 278 | .iter() 279 | .cloned() 280 | .map(|lifetime_def| TypeParamBound::Lifetime(lifetime_def.lifetime)), 281 | ) 282 | .collect(), 283 | impl_token: Token!(impl)(Span::call_site()), 284 | })), 285 | type_token: Token!(type)(Span::call_site()), 286 | }); 287 | 288 | let existential_type_path_for_impl = Path { 289 | // self::__real_async_trait_impl_ExistentialTypeFor_FUNCTIONNAME 290 | leading_colon: None, 291 | segments: vec![PathSegment { 292 | arguments: PathArguments::AngleBracketed(lifetime_angle_bracketed_bounds( 293 | toplevel_lifetimes 294 | .into_iter() 295 | .map(|lifetime_def| lifetime_def.lifetime), 296 | )), 297 | ident: Ident::new(&existential_type_name, Span::call_site()), 298 | }] 299 | .into_iter() 300 | .collect(), 301 | }; 302 | let existential_path_type = Type::Path(TypePath { 303 | path: existential_type_path_for_impl, 304 | qself: None, 305 | }); 306 | 307 | let gat_ident = gat_ident_for_sig(&method.sig); 308 | 309 | gat_defs.push(ImplItemType { 310 | attrs: Vec::new(), 311 | defaultness: None, 312 | eq_token: Token!(=)(Span::call_site()), 313 | generics: Generics { 314 | lt_token: Some(Token!(<)(Span::call_site())), 315 | gt_token: Some(Token!(>)(Span::call_site())), 316 | where_clause: Some(WhereClause { 317 | where_token: Where::default(), 318 | predicates: function_lifetimes 319 | .iter() 320 | .cloned() 321 | .map(|lifetimedef| { 322 | WherePredicate::Type(PredicateType { 323 | colon_token: Token!(:)(Span::call_site()), 324 | lifetimes: None, 325 | bounded_ty: Type::Path(TypePath { 326 | qself: None, 327 | path: Path { 328 | leading_colon: None, 329 | segments: Punctuated::from_iter([PathSegment { 330 | ident: Ident::new("Self", Span::call_site()), 331 | arguments: PathArguments::None, 332 | }]), 333 | }, 334 | }), 335 | bounds: Punctuated::from_iter([TypeParamBound::Lifetime( 336 | lifetimedef.lifetime, 337 | )]), 338 | }) 339 | }) 340 | .collect(), 341 | }), 342 | params: function_lifetimes 343 | .iter() 344 | .cloned() 345 | .map(GenericParam::Lifetime) 346 | .collect(), 347 | }, 348 | ident: gat_ident.clone(), 349 | semi_token: Token!(;)(Span::call_site()), 350 | ty: existential_path_type.clone(), 351 | type_token: Token!(type)(Span::call_site()), 352 | vis: Visibility::Inherited, 353 | }); 354 | 355 | let gat_self_type = self_gat_type( 356 | gat_ident, 357 | function_lifetimes 358 | .into_iter() 359 | .map(|lifetime_def| lifetime_def.lifetime), 360 | ); 361 | 362 | method.sig.output = ReturnType::Type( 363 | Token!(->)(Span::call_site()), 364 | Box::new(gat_self_type.into()), 365 | ); 366 | 367 | let method_stmts = mem::take(&mut method.block.stmts); 368 | 369 | method.block.stmts = vec![Stmt::Expr(Expr::Async(ExprAsync { 370 | async_token: Token!(async)(Span::call_site()), 371 | attrs: Vec::new(), 372 | block: Block { 373 | brace_token: token::Brace { 374 | span: Span::call_site(), 375 | }, 376 | stmts: method_stmts, 377 | }, 378 | capture: Some(Token!(move)(Span::call_site())), 379 | }))]; 380 | } 381 | 382 | item.items.extend(gat_defs.into_iter().map(Into::into)); 383 | 384 | quote! { 385 | 386 | const _: () = { 387 | #item 388 | 389 | #(#existential_type_defs)* 390 | }; 391 | } 392 | } 393 | 394 | fn return_type(retval: ReturnType) -> Type { 395 | match retval { 396 | ReturnType::Default => Type::Tuple(TypeTuple { 397 | elems: Punctuated::new(), 398 | paren_token: token::Paren { 399 | span: Span::call_site(), 400 | }, 401 | }), 402 | ReturnType::Type(_, ty) => *ty, 403 | } 404 | } 405 | 406 | fn future_trait_bound(fn_output_ty: Type) -> TraitBound { 407 | const FUTURE_TRAIT_PATH_STR: &str = "::core::future::Future"; 408 | const FUTURE_TRAIT_OUTPUT_IDENT_STR: &str = "Output"; 409 | 410 | let mut future_trait_path = 411 | syn::parse2::(TokenStream::from_str(FUTURE_TRAIT_PATH_STR).unwrap()) 412 | .expect("failed to parse `::core::future::Future` as a syn `Path`"); 413 | 414 | let future_angle_bracketed_args = AngleBracketedGenericArguments { 415 | colon2_token: None, // FIXME 416 | lt_token: Token!(<)(Span::call_site()), 417 | gt_token: Token!(>)(Span::call_site()), 418 | args: iter::once(GenericArgument::Binding(Binding { 419 | ident: Ident::new(FUTURE_TRAIT_OUTPUT_IDENT_STR, Span::call_site()), 420 | eq_token: Token!(=)(Span::call_site()), 421 | ty: fn_output_ty, 422 | })) 423 | .collect(), 424 | }; 425 | 426 | future_trait_path 427 | .segments 428 | .last_mut() 429 | .expect("Expected ::core::future::Future to have `Future` as the last segment") 430 | .arguments = PathArguments::AngleBracketed(future_angle_bracketed_args); 431 | 432 | TraitBound { 433 | // for TraitBounds, these are HRTBs, which are useless since there are already GATs present 434 | lifetimes: None, 435 | // This is not ?Sized or something like that 436 | modifier: TraitBoundModifier::None, 437 | paren_token: None, 438 | path: future_trait_path, 439 | } 440 | } 441 | fn send_trait_bound() -> TraitBound { 442 | const SEND_TRAIT_PATH_STR: &str = "::core::marker::Send"; 443 | 444 | let send_trait_path = syn::parse2::(TokenStream::from_str(SEND_TRAIT_PATH_STR).unwrap()) 445 | .expect("failed to parse `::core::marker::Send` as a syn `Path`"); 446 | 447 | TraitBound { 448 | lifetimes: None, 449 | modifier: TraitBoundModifier::None, 450 | paren_token: None, 451 | path: send_trait_path, 452 | } 453 | } 454 | 455 | fn validate_that_function_always_has_lifetimes(signature: &Signature) { 456 | for input in signature.inputs.iter() { 457 | match input { 458 | FnArg::Receiver(ref recv) => { 459 | if let Some((_ampersand, _lifetime @ None)) = &recv.reference { 460 | panic!("{}self parameter lacked an explicit lifetime, which is required by this proc macro", if recv.mutability.is_some() { "&mut " } else { "&" }); 461 | } 462 | } 463 | FnArg::Typed(PatType { ref ty, .. }) => { 464 | syn::visit::visit_type(&mut LifetimeVisitor, ty) 465 | } 466 | } 467 | } 468 | if let ReturnType::Type(_, ref ty) = signature.output { 469 | syn::visit::visit_type(&mut LifetimeVisitor, ty); 470 | }; 471 | } 472 | fn already_defined_lifetimes( 473 | toplevel_generics: &Generics, 474 | method_generics: &Generics, 475 | ) -> (Vec, Vec) { 476 | //Global scope 477 | //let mut lifetimes = vec! [LifetimeDef::new(Lifetime::new("'static", Span::call_site()))]; 478 | 479 | let mut lifetimes = Vec::new(); 480 | // Trait definition scope 481 | lifetimes.extend(toplevel_generics.lifetimes().cloned()); 482 | // Function definition scope 483 | let function_lifetimes = method_generics.lifetimes().cloned().collect::>(); 484 | lifetimes.extend(function_lifetimes.iter().cloned()); 485 | (lifetimes, function_lifetimes) 486 | } 487 | fn lifetime_angle_bracketed_bounds( 488 | lifetimes: impl IntoIterator, 489 | ) -> AngleBracketedGenericArguments { 490 | AngleBracketedGenericArguments { 491 | colon2_token: None, 492 | lt_token: Token!(<)(Span::call_site()), 493 | gt_token: Token!(>)(Span::call_site()), 494 | args: lifetimes 495 | .into_iter() 496 | .map(GenericArgument::Lifetime) 497 | .collect(), 498 | } 499 | } 500 | fn gat_ident_for_sig(sig: &Signature) -> Ident { 501 | let gat_name = format!("__real_async_trait_impl_TypeFor_{}", sig.ident); 502 | Ident::new(&gat_name, Span::call_site()) 503 | } 504 | fn self_gat_type( 505 | gat_ident: Ident, 506 | function_lifetimes: impl IntoIterator, 507 | ) -> TypePath { 508 | TypePath { 509 | path: Path { 510 | // represents the pattern Self::GAT_NAME... 511 | leading_colon: None, 512 | segments: vec![ 513 | PathSegment { 514 | ident: Ident::new("Self", Span::call_site()), 515 | arguments: PathArguments::None, 516 | }, 517 | PathSegment { 518 | ident: gat_ident, 519 | arguments: PathArguments::AngleBracketed(lifetime_angle_bracketed_bounds( 520 | function_lifetimes, 521 | )), 522 | }, 523 | ] 524 | .into_iter() 525 | .collect(), 526 | }, 527 | qself: None, 528 | } 529 | } 530 | fn method_requires_send(attrs: &mut Vec) -> bool { 531 | if let Some(first_attr_idx) = attrs.iter().position(|attr| { 532 | // This represents #[send], which will be the most common way to express the Send 533 | // future bound. 534 | let is_direct_send = attr.path.is_ident("send"); 535 | 536 | // However, in some edge-cases, it may not be impossible that this proc macro is 537 | // combined with another one, that has a special use-case for `#[send]`. Thus, we will 538 | // also allow the user to type `#[::real_async_trait::send]` instead. 539 | let is_indirect_send = { 540 | let has_leading_colon = attr.path.leading_colon.is_some(); 541 | let corresponding_segments = [ 542 | PathSegment { 543 | ident: Ident::new("real_async_trait", Span::call_site()), 544 | arguments: PathArguments::None, 545 | }, 546 | PathSegment { 547 | ident: Ident::new("send", Span::call_site()), 548 | arguments: PathArguments::None, 549 | }, 550 | ]; 551 | 552 | has_leading_colon 553 | && Iterator::eq(attr.path.segments.iter(), corresponding_segments.iter()) 554 | }; 555 | 556 | is_direct_send || is_indirect_send 557 | }) { 558 | let attr = attrs.remove(first_attr_idx); 559 | assert_eq!(attr.style, AttrStyle::Outer); 560 | true 561 | } else { 562 | false 563 | } 564 | } 565 | fn handle_item_trait(mut item: ItemTrait) -> TokenStream { 566 | let mut new_gat_items = Vec::new(); 567 | 568 | // Loop through every single async fn declared in the trait. 569 | for method in item 570 | .items 571 | .iter_mut() 572 | .filter_map(|item| { 573 | if let TraitItem::Method(func) = item { 574 | Some(func) 575 | } else { 576 | None 577 | } 578 | }) 579 | .filter(|method| method.sig.asyncness.is_some()) 580 | { 581 | // For each async fn, remove the async part, replace the return value with a generic 582 | // associated type, and add that generic associated type to the trait item. 583 | 584 | // Check that all types have a lifetime that is either specific to the trait item, or 585 | // to the current function (or 'static). Any other lifetime will and must produce a 586 | // compiler error. 587 | let gat_ident = gat_ident_for_sig(&method.sig); 588 | 589 | let requires_send = method_requires_send(&mut method.attrs); 590 | let method_return_ty = return_type(method.sig.output.clone()); 591 | 592 | validate_that_function_always_has_lifetimes(&method.sig); 593 | 594 | method.sig.asyncness = None; 595 | 596 | let (toplevel_lifetimes, function_lifetimes) = 597 | already_defined_lifetimes(&item.generics, &method.sig.generics); 598 | 599 | new_gat_items.push(TraitItemType { 600 | attrs: Vec::new(), 601 | type_token: Token!(type)(Span::call_site()), 602 | bounds: iter::once(TypeParamBound::Trait(future_trait_bound(method_return_ty))) 603 | .chain(if requires_send { 604 | Some(TypeParamBound::Trait(send_trait_bound())) 605 | } else { 606 | None 607 | }) 608 | .chain( 609 | toplevel_lifetimes 610 | .into_iter() 611 | .map(|lifetime_def| lifetime_def.lifetime) 612 | .map(TypeParamBound::Lifetime), 613 | ) 614 | .collect(), 615 | colon_token: Some(Token!(:)(Span::call_site())), 616 | default: None, 617 | generics: Generics { 618 | lt_token: Some(Token!(<)(Span::call_site())), 619 | gt_token: Some(Token!(>)(Span::call_site())), 620 | where_clause: Some(WhereClause { 621 | where_token: Where::default(), 622 | predicates: function_lifetimes 623 | .iter() 624 | .cloned() 625 | .map(|lifetimedef| { 626 | WherePredicate::Type(PredicateType { 627 | colon_token: Token!(:)(Span::call_site()), 628 | lifetimes: None, 629 | bounded_ty: Type::Path(TypePath { 630 | qself: None, 631 | path: Path { 632 | leading_colon: None, 633 | segments: Punctuated::from_iter([PathSegment { 634 | ident: Ident::new("Self", Span::call_site()), 635 | arguments: PathArguments::None, 636 | }]), 637 | }, 638 | }), 639 | bounds: Punctuated::from_iter([TypeParamBound::Lifetime( 640 | lifetimedef.lifetime, 641 | )]), 642 | }) 643 | }) 644 | .collect(), 645 | }), 646 | params: function_lifetimes 647 | .iter() 648 | .cloned() 649 | .map(GenericParam::Lifetime) 650 | .collect(), 651 | }, 652 | ident: gat_ident.clone(), 653 | semi_token: Token!(;)(Span::call_site()), 654 | }); 655 | 656 | let self_gat_type = self_gat_type( 657 | gat_ident, 658 | function_lifetimes 659 | .into_iter() 660 | .map(|lifetime_def| lifetime_def.lifetime), 661 | ); 662 | 663 | method.sig.output = ReturnType::Type( 664 | Token!(->)(Span::call_site()), 665 | Box::new(self_gat_type.into()), 666 | ); 667 | } 668 | item.items 669 | .extend(new_gat_items.into_iter().map(TraitItem::Type)); 670 | 671 | quote! { 672 | #item 673 | } 674 | } 675 | fn real_async_trait2(_args_stream: TokenStream, token_stream: TokenStream) -> TokenStream { 676 | // The #[real_async_trait] attribute macro, is applicable to both trait blocks, and to impl 677 | // blocks that operate on that trait. 678 | 679 | if let Ok(item_trait) = syn::parse2::(token_stream.clone()) { 680 | handle_item_trait(item_trait) 681 | } else if let Ok(item_impl) = syn::parse2::(token_stream) { 682 | handle_item_impl(item_impl) 683 | } else { 684 | panic!("expected either a trait or an impl item") 685 | } 686 | } 687 | 688 | /// A proc macro that supports using async fn in traits and trait impls. Refer to the top-level 689 | /// crate documentation for more information. 690 | #[proc_macro_attribute] 691 | pub fn real_async_trait( 692 | args_stream: proc_macro::TokenStream, 693 | token_stream: proc_macro::TokenStream, 694 | ) -> proc_macro::TokenStream { 695 | real_async_trait2(args_stream.into(), token_stream.into()).into() 696 | } 697 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn correct_trait_output() { 3 | let input = quote::quote! { 4 | pub trait RedoxScheme { 5 | #[send] 6 | async fn open<'a>(&'a mut self, path: &'a [u8], flags: usize) -> Result; 7 | async fn read<'a>(&'a mut self, fd: usize, buf: &'a mut [u8]) -> Result; 8 | async fn write<'a>(&'a mut self, fd: usize, buf: &'a [u8]) -> Result; 9 | async fn close<'a>(&'a mut self, fd: usize) -> Result<(), Errno>; 10 | } 11 | }; 12 | let expected_output = quote::quote! { 13 | pub trait RedoxScheme { 14 | fn open<'a>(&'a mut self, path: &'a [u8], flags: usize) -> Self::__real_async_trait_impl_TypeFor_open<'a>; 15 | fn read<'a>(&'a mut self, fd: usize, buf: &'a mut [u8]) -> Self::__real_async_trait_impl_TypeFor_read<'a>; 16 | fn write<'a>(&'a mut self, fd: usize, buf: &'a [u8]) -> Self::__real_async_trait_impl_TypeFor_write<'a>; 17 | fn close<'a>(&'a mut self, fd: usize) -> Self::__real_async_trait_impl_TypeFor_close<'a>; 18 | 19 | type __real_async_trait_impl_TypeFor_open<'a>: ::core::future::Future> + ::core::marker::Send + 'a where Self: 'a; 20 | type __real_async_trait_impl_TypeFor_read<'a>: ::core::future::Future> + 'a where Self: 'a; 21 | type __real_async_trait_impl_TypeFor_write<'a>: ::core::future::Future> + 'a where Self: 'a; 22 | type __real_async_trait_impl_TypeFor_close<'a>: ::core::future::Future> + 'a where Self: 'a; 23 | } 24 | }; 25 | let actual_output = crate::real_async_trait2(proc_macro2::TokenStream::new(), input); 26 | 27 | // TODO: Any better way to do this? 28 | let expected_output_trait = syn::parse2::(expected_output).unwrap(); 29 | let actual_output_trait = syn::parse2::(actual_output).unwrap(); 30 | 31 | assert_eq!(expected_output_trait, actual_output_trait); 32 | } 33 | #[test] 34 | fn correct_impl_output() { 35 | let input = quote::quote! { 36 | impl RedoxScheme for MyType { 37 | #[send] 38 | async fn open<'a>(&'a mut self, path: &'a [u8], flags: usize) -> Result { 39 | Ok(0) 40 | } 41 | async fn read<'a>(&'a mut self, fd: usize, buf: &'a mut [u8]) -> Result { 42 | Ok(0) 43 | } 44 | async fn write<'a>(&'a mut self, fd: usize, buf: &'a [u8]) -> Result { 45 | Ok(0) 46 | } 47 | async fn close<'a>(&'a mut self, fd: usize) -> Result<(), Errno> { 48 | Ok(()) 49 | } 50 | } 51 | }; 52 | let expected_output = quote::quote! { 53 | const _: () = { 54 | impl RedoxScheme for MyType { 55 | fn open<'a>(&'a mut self, path: &'a [u8], flags: usize) -> Self::__real_async_trait_impl_TypeFor_open<'a> { 56 | async move { Ok(0) } 57 | } 58 | fn read<'a>(&'a mut self, fd: usize, buf: &'a mut [u8]) -> Self::__real_async_trait_impl_TypeFor_read<'a> { 59 | async move { Ok(0) } 60 | } 61 | fn write<'a>(&'a mut self, fd: usize, buf: &'a [u8]) -> Self::__real_async_trait_impl_TypeFor_write<'a> { 62 | async move { Ok(0) } 63 | } 64 | fn close<'a>(&'a mut self, fd: usize) -> Self::__real_async_trait_impl_TypeFor_close<'a> { 65 | async move { Ok(()) } 66 | } 67 | 68 | type __real_async_trait_impl_TypeFor_open<'a> = __real_async_trait_impl_ExistentialTypeFor_open<'a> where Self: 'a; 69 | type __real_async_trait_impl_TypeFor_read<'a> = __real_async_trait_impl_ExistentialTypeFor_read<'a> where Self: 'a; 70 | type __real_async_trait_impl_TypeFor_write<'a> = __real_async_trait_impl_ExistentialTypeFor_write<'a> where Self: 'a; 71 | type __real_async_trait_impl_TypeFor_close<'a> = __real_async_trait_impl_ExistentialTypeFor_close<'a> where Self: 'a; 72 | } 73 | type __real_async_trait_impl_ExistentialTypeFor_open<'a> = impl ::core::future::Future> + ::core::marker::Send + 'a; 74 | type __real_async_trait_impl_ExistentialTypeFor_read<'a> = impl ::core::future::Future> + 'a; 75 | type __real_async_trait_impl_ExistentialTypeFor_write<'a> = impl ::core::future::Future> + 'a; 76 | type __real_async_trait_impl_ExistentialTypeFor_close<'a> = impl ::core::future::Future> + 'a; 77 | }; 78 | }; 79 | let actual_output = crate::real_async_trait2(proc_macro2::TokenStream::new(), input); 80 | 81 | // TODO: Any better way to do this? 82 | let expected_output_trait = syn::parse2::(expected_output).unwrap(); 83 | let actual_output_trait = syn::parse2::(actual_output).unwrap(); 84 | 85 | if expected_output_trait != actual_output_trait { 86 | use quote::ToTokens; 87 | panic!( 88 | "\n\n\nEXPECTED {}\n\n\nFOUND {}\n\n\n", 89 | expected_output_trait.into_token_stream(), 90 | actual_output_trait.into_token_stream() 91 | ); 92 | } 93 | } 94 | 95 | // TODO: Expand tests, and add integration tests. 96 | --------------------------------------------------------------------------------