├── .gitignore ├── .gitattributes ├── tests ├── ui-pass │ ├── allowed-struct.rs │ ├── allowed-impl-block.rs │ ├── preserves-associated-items.rs │ ├── generics-struct.rs │ ├── placing-builds.rs │ ├── nested.rs.disabled │ ├── regular-constructor.rs │ ├── placing-runs.rs │ ├── placing-box.rs │ ├── placing-body.rs │ └── placing-both.rs.disabled ├── ui-fail │ ├── E0001-invalid-item-kind.rs │ ├── E0003-invalid-impl-target.rs │ ├── E0002-trait-impls-unsupported.rs │ ├── E0007-invalid-placing-target.rs │ ├── E0005-empty-constructor-body.rs │ ├── E0006-invalid-constructor-body-non-expr.rs │ ├── E0006-invalid-constructor-body-non-struct.rs │ ├── E0008-invalid-pointer-constructor.rs │ ├── E0004-invalid-attr.rs │ ├── E0001-invalid-item-kind.stderr │ ├── E0002-trait-impls-unsupported.stderr │ ├── E0003-invalid-impl-target.stderr │ ├── E0004-invalid-attr.stderr │ ├── E0007-invalid-placing-target.stderr │ ├── E0008-invalid-pointer-constructor.stderr │ ├── E0005-empty-constructor-body.stderr │ ├── E0006-invalid-constructor-body-non-expr.stderr │ └── E0006-invalid-constructor-body-non-struct.stderr └── ui.rs ├── examples └── basic.rs ├── Cargo.toml ├── src ├── inherent │ ├── placing │ │ ├── mod.rs │ │ ├── inline.rs │ │ └── pointer.rs │ ├── moving.rs │ ├── fn_kind.rs │ └── mod.rs ├── lib.rs ├── strukt.rs └── utils.rs ├── LICENSE-MIT ├── .github ├── workflows │ └── ci.yaml ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | tmp/ 3 | Cargo.lock 4 | .DS_Store 5 | wip/ 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /tests/ui-pass/allowed-struct.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests/ui-fail/E0001-invalid-item-kind.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | union Cat { 3 | age: u8, 4 | } 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/ui-pass/allowed-impl-block.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat {} 3 | 4 | #[placing::placing] 5 | impl Cat {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/ui-fail/E0003-invalid-impl-target.rs: -------------------------------------------------------------------------------- 1 | trait Meow { 2 | type Sound; 3 | } 4 | 5 | #[placing::placing] 6 | impl [Cat] {} 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/ui-fail/E0002-trait-impls-unsupported.rs: -------------------------------------------------------------------------------- 1 | struct Cat { 2 | age: u8, 3 | } 4 | 5 | trait Meow {} 6 | 7 | #[placing::placing] 8 | impl Meow for Cat {} 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests/ui-fail/E0007-invalid-placing-target.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat; 3 | 4 | #[placing::placing] 5 | impl Cat { 6 | #[placing] 7 | fn new(&self) {} 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests/ui-fail/E0005-empty-constructor-body.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat; 3 | 4 | #[placing::placing] 5 | impl Cat { 6 | #[placing] 7 | fn new() -> Self {} 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests/ui.rs: -------------------------------------------------------------------------------- 1 | // set `TRYBUILD=overwrite` to update the stdout output 2 | #[test] 3 | fn ui() { 4 | let t = trybuild::TestCases::new(); 5 | t.compile_fail("tests/ui-fail/*.rs"); 6 | t.pass("tests/ui-pass/*.rs"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/ui-pass/preserves-associated-items.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat {} 3 | 4 | #[placing::placing] 5 | impl Cat { 6 | const NAME: &str = "chashu"; 7 | } 8 | 9 | fn main() { 10 | assert_eq!(Cat::NAME, "chashu"); 11 | } 12 | -------------------------------------------------------------------------------- /tests/ui-fail/E0006-invalid-constructor-body-non-expr.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat; 3 | 4 | #[placing::placing] 5 | impl Cat { 6 | #[placing] 7 | fn new() -> Self { 8 | todo!() 9 | } 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /tests/ui-fail/E0006-invalid-constructor-body-non-struct.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat; 3 | 4 | #[placing::placing] 5 | impl Cat { 6 | #[placing] 7 | fn new() -> Self { 8 | { 9 | Self {} 10 | } 11 | } 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /tests/ui-fail/E0008-invalid-pointer-constructor.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Box { 10 | Box::new() 11 | } 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /tests/ui-pass/generics-struct.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat 3 | where 4 | J: Send, 5 | { 6 | k: K, 7 | j: J, 8 | foo: [u8; N], 9 | } 10 | 11 | #[placing::placing] 12 | impl Cat where J: Send {} 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /tests/ui-pass/placing-builds.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Self { 10 | Self { age } 11 | } 12 | 13 | fn age(&self) -> &u8 { 14 | &self.age 15 | } 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /tests/ui-fail/E0004-invalid-attr.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat; 3 | 4 | #[placing::placing] 5 | impl Cat { 6 | #[placing(invalid)] 7 | fn list(&self) -> Self { 8 | todo!() 9 | } 10 | 11 | #[placing = "invalid"] 12 | fn path(&self) -> Self { 13 | todo!() 14 | } 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /tests/ui-pass/nested.rs.disabled: -------------------------------------------------------------------------------- 1 | #[placing::emplace] 2 | struct Bed { 3 | #[emplace] 4 | cat: Cat, 5 | } 6 | 7 | #[placing::emplace] 8 | struct Cat { 9 | age: u8, 10 | } 11 | 12 | #[placing::emplace] 13 | impl Cat { 14 | fn age(&self) -> &u8 { 15 | &self.age 16 | } 17 | } 18 | 19 | fn main() { 20 | // let cat = placing::new!(Cat, 12); 21 | } 22 | -------------------------------------------------------------------------------- /tests/ui-pass/regular-constructor.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | fn new(age: u8) -> Self { 9 | Self { age } 10 | } 11 | 12 | fn age(&self) -> &u8 { 13 | &self.age 14 | } 15 | } 16 | 17 | fn main() { 18 | let cat = Cat::new(12); 19 | assert_eq!(cat.age(), &12); 20 | } 21 | -------------------------------------------------------------------------------- /tests/ui-fail/E0001-invalid-item-kind.stderr: -------------------------------------------------------------------------------- 1 | error: `[placing, E0001] invalid item kind: macro `placing` can only be used on inherent impls and structs 2 | --> tests/ui-fail/E0001-invalid-item-kind.rs:1:1 3 | | 4 | 1 | #[placing::placing] 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `placing::placing` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests/ui-fail/E0002-trait-impls-unsupported.stderr: -------------------------------------------------------------------------------- 1 | error: `[placing, E0002] trait impls unsupported: macro `placing` can only be used on bare `impl {}` blocks 2 | --> tests/ui-fail/E0002-trait-impls-unsupported.rs:7:1 3 | | 4 | 7 | #[placing::placing] 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `placing::placing` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests/ui-pass/placing-runs.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Self { 10 | Self { age } 11 | } 12 | 13 | fn age(&self) -> &u8 { 14 | &self.age 15 | } 16 | } 17 | 18 | fn main() { 19 | let mut cat = unsafe { Cat::placing_uninit_new() }; 20 | unsafe { cat.placing_init_new(12) }; 21 | assert_eq!(cat.age(), &12); 22 | } 23 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Box { 10 | Box::new(Self { age }) 11 | } 12 | 13 | fn age(&self) -> &u8 { 14 | &self.age 15 | } 16 | } 17 | 18 | fn main() { 19 | let mut cat = unsafe { Cat::placing_uninit_new() }; 20 | unsafe { Cat::placing_init_new(&mut cat, 12) }; 21 | assert_eq!(cat.age(), &12); 22 | } 23 | -------------------------------------------------------------------------------- /tests/ui-pass/placing-box.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Box { 10 | Box::new(Self { age }) 11 | } 12 | 13 | fn age(&self) -> &u8 { 14 | &self.age 15 | } 16 | } 17 | 18 | fn main() { 19 | let mut cat = unsafe { Cat::placing_uninit_new() }; 20 | unsafe { cat.placing_init_new(12) }; 21 | assert_eq!(cat.age(), &12); 22 | } 23 | -------------------------------------------------------------------------------- /tests/ui-pass/placing-body.rs: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Self { 10 | let age = age * 2; 11 | Self { age } 12 | } 13 | 14 | fn age(&self) -> &u8 { 15 | &self.age 16 | } 17 | } 18 | 19 | fn main() { 20 | let mut cat = unsafe { Cat::placing_uninit_new() }; 21 | unsafe { cat.placing_init_new(12) }; 22 | assert_eq!(cat.age(), &24); 23 | } 24 | -------------------------------------------------------------------------------- /tests/ui-pass/placing-both.rs.disabled: -------------------------------------------------------------------------------- 1 | #[placing::placing] 2 | struct Cat { 3 | age: u8, 4 | } 5 | 6 | #[placing::placing] 7 | impl Cat { 8 | #[placing] 9 | fn new(age: u8) -> Self { 10 | Self { age } 11 | } 12 | 13 | fn age(&self) -> &u8 { 14 | &self.age 15 | } 16 | } 17 | 18 | fn main() { 19 | let mut cat = unsafe { Cat::placing_uninit_new() }; 20 | cat.placing_init_new(12); 21 | assert_eq!(cat.age(), &12); 22 | 23 | let cat = Cat::new(12); 24 | assert_eq!(cat.age(), &12); 25 | } 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "placing" 3 | version = "1.0.0" 4 | license = "MIT OR Apache-2.0" 5 | repository = "https://github.com/yoshuawuyts/placing" 6 | documentation = "https://docs.rs/placing" 7 | description = "A prototype notation for referentially stable constructors" 8 | readme = "README.md" 9 | edition = "2021" 10 | keywords = [] 11 | categories = [] 12 | authors = [ 13 | "Yoshua Wuyts " 14 | ] 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [features] 20 | 21 | [dependencies] 22 | proc-macro2 = "1.0.92" 23 | quote = "1.0.37" 24 | syn = { version = "2.0.91", features = ["extra-traits", "full"] } 25 | 26 | [dev-dependencies] 27 | trybuild = "1.0.101" 28 | -------------------------------------------------------------------------------- /src/inherent/placing/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use syn::{spanned::Spanned, ImplItemFn}; 3 | 4 | use crate::utils; 5 | 6 | use super::ImplFns; 7 | 8 | mod inline; 9 | mod pointer; 10 | 11 | /// Rewrite a `#[placing]` statement to create the inner type instead 12 | pub(crate) fn rewrite_placing_constructor( 13 | output: &mut ImplFns, 14 | f: ImplItemFn, 15 | ident: &syn::Ident, 16 | ) -> Result<(), TokenStream> { 17 | match utils::constructor_type(&f.sig, ident) { 18 | utils::ConstructorKind::Inline => inline::inline_constructor(output, f), 19 | utils::ConstructorKind::Pointer(kind) => pointer::pointer_constructor(output, f, kind), 20 | utils::ConstructorKind::Other => { 21 | return Err(quote::quote_spanned! { f.sig.output.span() => 22 | compile_error!("[E0009, placing] invalid constructor return type"), 23 | } 24 | .into()) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/ui-fail/E0003-invalid-impl-target.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0003-invalid-impl-target.rs:6:1 3 | | 4 | 6 | impl [Cat] {} 5 | | ^^^^ 6 | | 7 | help: change the delimiters to curly braces 8 | | 9 | 6 - impl [Cat] {} 10 | 6 + {} [Cat] {} 11 | | 12 | help: add a semicolon 13 | | 14 | 6 | impl; [Cat] {} 15 | | + 16 | 17 | error: macro expansion ignores `,` and any tokens following 18 | --> tests/ui-fail/E0003-invalid-impl-target.rs:6:1 19 | | 20 | 5 | #[placing::placing] 21 | | ------------------- caused by the macro expansion here 22 | 6 | impl [Cat] {} 23 | | ^^^^ 24 | | 25 | = note: the usage of `placing::placing!` is likely invalid in item context 26 | 27 | error: [E0003, placing] invalid impl target: `placing` doesn't work for impls on tuples, slices, or other non-path types 28 | --> tests/ui-fail/E0003-invalid-impl-target.rs:6:1 29 | | 30 | 6 | impl [Cat] {} 31 | | ^^^^ 32 | -------------------------------------------------------------------------------- /tests/ui-fail/E0004-invalid-attr.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0004-invalid-attr.rs:6:5 3 | | 4 | 6 | #[placing(invalid)] 5 | | ^ 6 | | 7 | help: change the delimiters to curly braces 8 | | 9 | 6 - #[placing(invalid)] 10 | 6 + {}[placing(invalid)] 11 | | 12 | help: add a semicolon 13 | | 14 | 6 | #;[placing(invalid)] 15 | | + 16 | 17 | error: macro expansion ignores `,` and any tokens following 18 | --> tests/ui-fail/E0004-invalid-attr.rs:6:5 19 | | 20 | 4 | #[placing::placing] 21 | | ------------------- caused by the macro expansion here 22 | 5 | impl Cat { 23 | 6 | #[placing(invalid)] 24 | | ^ 25 | | 26 | = note: the usage of `placing::placing!` is likely invalid in item context 27 | 28 | error: [E0004, placing] invalid attr: the #[placing] attribute does not support any additional arguments 29 | --> tests/ui-fail/E0004-invalid-attr.rs:6:5 30 | | 31 | 6 | #[placing(invalid)] 32 | | ^ 33 | -------------------------------------------------------------------------------- /tests/ui-fail/E0007-invalid-placing-target.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0007-invalid-placing-target.rs:7:5 3 | | 4 | 7 | fn new(&self) {} 5 | | ^^ 6 | | 7 | help: change the delimiters to curly braces 8 | | 9 | 7 - fn new(&self) {} 10 | 7 + {} new(&self) {} 11 | | 12 | help: add a semicolon 13 | | 14 | 7 | fn; new(&self) {} 15 | | + 16 | 17 | error: macro expansion ignores `,` and any tokens following 18 | --> tests/ui-fail/E0007-invalid-placing-target.rs:7:5 19 | | 20 | 4 | #[placing::placing] 21 | | ------------------- caused by the macro expansion here 22 | ... 23 | 7 | fn new(&self) {} 24 | | ^^ 25 | | 26 | = note: the usage of `placing::placing!` is likely invalid in item context 27 | 28 | error: [E0007, placing] invalid placing target: the #[placing] attribute cannot be applied to static functions 29 | --> tests/ui-fail/E0007-invalid-placing-target.rs:7:5 30 | | 31 | 7 | fn new(&self) {} 32 | | ^^ 33 | -------------------------------------------------------------------------------- /tests/ui-fail/E0008-invalid-pointer-constructor.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0008-invalid-pointer-constructor.rs:10:9 3 | | 4 | 10 | Box::new() 5 | | ^^^ 6 | | 7 | help: change the delimiters to curly braces 8 | | 9 | 10 - Box::new() 10 | 10 + {}::new() 11 | | 12 | help: add a semicolon 13 | | 14 | 10 | Box;::new() 15 | | + 16 | 17 | error: macro expansion ignores `,` and any tokens following 18 | --> tests/ui-fail/E0008-invalid-pointer-constructor.rs:10:9 19 | | 20 | 6 | #[placing::placing] 21 | | ------------------- caused by the macro expansion here 22 | ... 23 | 10 | Box::new() 24 | | ^^^ 25 | | 26 | = note: the usage of `placing::placing!` is likely invalid in item context 27 | 28 | error: [E0008, placing] invalid pointer constructor` 29 | --> tests/ui-fail/E0008-invalid-pointer-constructor.rs:10:9 30 | | 31 | 10 | Box::new() 32 | | ^^^ 33 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A prototype notation for referentially stable constructors 2 | //! 3 | //! ## Tasks 4 | //! 5 | //! - [ ] support structs 6 | //! - [ ] support enums 7 | //! - [ ] support traits 8 | //! - [ ] support custom drop impls 9 | 10 | #![forbid(unsafe_code)] 11 | #![deny(missing_debug_implementations, nonstandard_style)] 12 | #![warn(missing_docs)] 13 | 14 | use proc_macro::TokenStream; 15 | use syn::parse_macro_input; 16 | 17 | mod inherent; 18 | mod strukt; 19 | mod utils; 20 | 21 | /// Enable methods to be constructed and operate in-place 22 | #[proc_macro_attribute] 23 | pub fn placing(_attr: TokenStream, item: TokenStream) -> TokenStream { 24 | let item = parse_macro_input!(item as syn::Item); 25 | match item { 26 | syn::Item::Impl(item) => inherent::process_impl(item), 27 | syn::Item::Struct(item) => strukt::process_struct(item), 28 | _ => quote::quote! { 29 | compile_error!("`[placing, E0001] invalid item kind: macro `placing` can only be used on inherent impls and structs"); 30 | }.into() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/ui-fail/E0005-empty-constructor-body.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0005-empty-constructor-body.rs:7:22 3 | | 4 | 7 | fn new() -> Self {} 5 | | ^^ 6 | | 7 | help: change the delimiters to curly braces 8 | | 9 | | 10 | help: add a semicolon 11 | | 12 | 7 | fn new() -> Self {}; 13 | | + 14 | 15 | error: macro expansion ignores `,` and any tokens following 16 | --> tests/ui-fail/E0005-empty-constructor-body.rs:7:22 17 | | 18 | 4 | #[placing::placing] 19 | | ------------------- caused by the macro expansion here 20 | ... 21 | 7 | fn new() -> Self {} 22 | | ^^ 23 | | 24 | = note: the usage of `placing::placing!` is likely invalid in item context 25 | 26 | error: [E0005, placing] empty constructor body: functions marked `#[placing]` cannot be empty 27 | --> tests/ui-fail/E0005-empty-constructor-body.rs:7:22 28 | | 29 | 7 | fn new() -> Self {} 30 | | ^^ 31 | -------------------------------------------------------------------------------- /tests/ui-fail/E0006-invalid-constructor-body-non-expr.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0006-invalid-constructor-body-non-expr.rs:8:9 3 | | 4 | 8 | todo!() 5 | | ^^^^ 6 | | 7 | help: change the delimiters to curly braces 8 | | 9 | 8 - todo!() 10 | 8 + {}!() 11 | | 12 | help: add a semicolon 13 | | 14 | 8 | todo;!() 15 | | + 16 | 17 | error: macro expansion ignores `,` and any tokens following 18 | --> tests/ui-fail/E0006-invalid-constructor-body-non-expr.rs:8:9 19 | | 20 | 4 | #[placing::placing] 21 | | ------------------- caused by the macro expansion here 22 | ... 23 | 8 | todo!() 24 | | ^^^^ 25 | | 26 | = note: the usage of `placing::placing!` is likely invalid in item context 27 | 28 | error: [E0006, placing] invalid constructor body: expected a struct expression as the last statement 29 | --> tests/ui-fail/E0006-invalid-constructor-body-non-expr.rs:8:9 30 | | 31 | 8 | todo!() 32 | | ^^^^ 33 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Yoshua Wuyts 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 | -------------------------------------------------------------------------------- /tests/ui-fail/E0006-invalid-constructor-body-non-struct.stderr: -------------------------------------------------------------------------------- 1 | error: macros that expand to items must be delimited with braces or followed by a semicolon 2 | --> tests/ui-fail/E0006-invalid-constructor-body-non-struct.rs:8:9 3 | | 4 | 8 | / { 5 | 9 | | Self {} 6 | 10 | | } 7 | | |_________^ 8 | | 9 | help: change the delimiters to curly braces 10 | | 11 | 8 ~ { 12 | 9 ~ } 13 | | 14 | help: add a semicolon 15 | | 16 | 10 | }; 17 | | + 18 | 19 | error: macro expansion ignores `,` and any tokens following 20 | --> tests/ui-fail/E0006-invalid-constructor-body-non-struct.rs:8:9 21 | | 22 | 4 | #[placing::placing] 23 | | ------------------- caused by the macro expansion here 24 | ... 25 | 8 | / { 26 | 9 | | Self {} 27 | 10 | | } 28 | | |_________^ 29 | | 30 | = note: the usage of `placing::placing!` is likely invalid in item context 31 | 32 | error: [E0006, placing] invalid constructor body: expected a struct expression as the last statement 33 | --> tests/ui-fail/E0006-invalid-constructor-body-non-struct.rs:8:9 34 | | 35 | 8 | / { 36 | 9 | | Self {} 37 | 10 | | } 38 | | |_________^ 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - staging 8 | - trying 9 | 10 | env: 11 | RUSTFLAGS: -Dwarnings 12 | 13 | jobs: 14 | build_and_test: 15 | name: Build and test 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest, macOS-latest] 20 | rust: [stable] 21 | 22 | steps: 23 | - uses: actions/checkout@master 24 | 25 | - name: Install ${{ matrix.rust }} 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: ${{ matrix.rust }} 29 | override: true 30 | 31 | - name: check 32 | uses: actions-rs/cargo@v1 33 | with: 34 | command: check 35 | args: --all --bins --examples 36 | 37 | - name: tests 38 | uses: actions-rs/cargo@v1 39 | with: 40 | command: test 41 | args: --all 42 | 43 | check_fmt_and_docs: 44 | name: Checking fmt and docs 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@master 48 | - uses: actions-rs/toolchain@v1 49 | with: 50 | toolchain: nightly 51 | components: rustfmt, clippy 52 | override: true 53 | 54 | - name: fmt 55 | run: cargo fmt --all -- --check 56 | 57 | - name: Docs 58 | run: cargo doc 59 | -------------------------------------------------------------------------------- /src/inherent/moving.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{spanned::Spanned, ImplItemFn}; 4 | 5 | use super::ImplFns; 6 | 7 | /// Rewrite a non-`#[placing]` statement to create the inner type instead 8 | pub(crate) fn rewrite_moving_constructor( 9 | output: &mut ImplFns, 10 | mut f: ImplItemFn, 11 | ident: &syn::Ident, 12 | ) -> Result<(), TokenStream> { 13 | let inner_ident = format_ident!("Inner{}", ident); 14 | let expr = match f.block.stmts.last_mut() { 15 | Some(syn::Stmt::Expr(expr, _)) => expr, 16 | Some(stmt) => return Err(quote::quote_spanned! { stmt.span() => 17 | compile_error!("[E0006, placing] invalid constructor body: functions marked `#[placing]` have to end with a struct expression"), 18 | }.into()), 19 | None => return Err(quote::quote_spanned! { f.block.span() => 20 | compile_error!("[E0005, placing] empty constructor body: functions marked `#[placing]` cannot be empty"), 21 | }.into()), 22 | }; 23 | 24 | match expr { 25 | syn::Expr::Struct(strukt) => { 26 | let fields = strukt.fields.clone(); 27 | *strukt = syn::parse2(quote! { 28 | #ident { 29 | inner: ::core::mem::MaybeUninit::new(#inner_ident { #fields }) 30 | } 31 | }).unwrap(); 32 | } 33 | expr => return Err(quote::quote_spanned! { expr.span() => 34 | compile_error!("[E0006, placing] invalid constructor body: functions marked `#[placing]` have to end with a struct expression"), 35 | }.into()), 36 | }; 37 | output.non_emplacing_constructors.push(f.into()); 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /src/strukt.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | use quote::{format_ident, quote}; 4 | use syn::ItemStruct; 5 | 6 | use crate::utils::create_maybe_generics; 7 | 8 | /// Process an impl block that carries the `#[placing]` notation 9 | pub(crate) fn process_struct(item: ItemStruct) -> TokenStream { 10 | // We need all the impl components to later recreate it 11 | // and fill it with our own methods 12 | let ItemStruct { 13 | attrs: _, 14 | vis, 15 | struct_token, 16 | ident, 17 | generics, 18 | fields, 19 | semi_token, 20 | } = item; 21 | 22 | let outer_generics = create_maybe_generics(&generics); 23 | let (outer_impl, outer_ty, outer_where) = outer_generics.split_for_impl(); 24 | 25 | let inner_ident = format_ident!("Inner{}", ident); 26 | let (inner_impl, inner_ty, inner_where) = generics.split_for_impl(); 27 | 28 | quote! { 29 | #vis #struct_token #ident #outer_impl 30 | #outer_where 31 | { inner: ::core::mem::MaybeUninit<#inner_ident #inner_ty> } 32 | 33 | #struct_token #inner_ident #inner_impl #inner_where 34 | #fields 35 | #semi_token 36 | 37 | impl #outer_impl ::core::ops::Deref for #ident #outer_ty #outer_where { 38 | type Target = #inner_ident #inner_ty; 39 | fn deref(&self) -> &Self::Target { 40 | unsafe { self.inner.assume_init_ref() } 41 | } 42 | } 43 | 44 | impl #outer_impl ::core::ops::DerefMut for #ident #outer_ty #outer_where { 45 | fn deref_mut(&mut self) -> &mut Self::Target { 46 | unsafe { self.inner.assume_init_mut() } 47 | } 48 | } 49 | 50 | impl #outer_impl Drop for #ident #outer_ty #outer_where { 51 | fn drop(&mut self) { 52 | unsafe { self.inner.assume_init_drop() } 53 | } 54 | } 55 | } 56 | .into() 57 | } 58 | -------------------------------------------------------------------------------- /src/inherent/fn_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::path_ident; 2 | use syn::{FnArg, Receiver, ReturnType, Signature}; 3 | 4 | /// What kind of function are we operating on? 5 | #[derive(Debug)] 6 | pub(crate) enum FunctionKind { 7 | /// A static method with no self-ty 8 | Static, 9 | /// A static constructor with a return type of `Self` 10 | Constructor(HeapTy), 11 | /// A method with a self-ty 12 | Method, 13 | /// A method with a self-ty that returns type `Self` 14 | Builder(HeapTy), 15 | } 16 | 17 | /// Is our `Self`-ty on the heap? 18 | #[derive(Debug)] 19 | pub(crate) enum HeapTy { 20 | /// `-> Self` 21 | None, 22 | /// `-> Box` 23 | Box, 24 | } 25 | 26 | impl FunctionKind { 27 | pub(crate) fn from_fn(sig: &Signature, self_ident: &syn::Ident) -> Self { 28 | // If the function contains `-> Self` or equivalent we're working with a 29 | // constructor 30 | if let ReturnType::Type(_, ty) = &sig.output { 31 | if let syn::Type::Path(type_path) = &**ty { 32 | let ty_path = path_ident(&type_path.path); 33 | if ty_path == "Self" || ty_path == self_ident.to_string() { 34 | match sig.inputs.first() { 35 | Some(FnArg::Receiver(Receiver { .. })) => { 36 | return Self::Builder(HeapTy::None) 37 | } 38 | _ => return Self::Constructor(HeapTy::None), 39 | }; 40 | } 41 | 42 | if ty_path == "Box < Self >" { 43 | match sig.inputs.first() { 44 | Some(FnArg::Receiver(Receiver { .. })) => { 45 | return Self::Builder(HeapTy::Box) 46 | } 47 | _ => return Self::Constructor(HeapTy::Box), 48 | }; 49 | } 50 | } 51 | } 52 | 53 | // If our function takes `self` or `self: Pattern {}`, we're working with a 54 | // method. Otherwise it's a static function. 55 | match sig.inputs.first() { 56 | Some(FnArg::Receiver(Receiver { .. })) => Self::Method, 57 | _ => Self::Static, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Contributions include code, documentation, answering user questions, running the 3 | project's infrastructure, and advocating for all types of users. 4 | 5 | The project welcomes all contributions from anyone willing to work in good faith 6 | with other contributors and the community. No contribution is too small and all 7 | contributions are valued. 8 | 9 | This guide explains the process for contributing to the project's GitHub 10 | Repository. 11 | 12 | - [Code of Conduct](#code-of-conduct) 13 | - [Bad Actors](#bad-actors) 14 | 15 | ## Code of Conduct 16 | The project has a [Code of Conduct](./CODE_OF_CONDUCT.md) that *all* 17 | contributors are expected to follow. This code describes the *minimum* behavior 18 | expectations for all contributors. 19 | 20 | As a contributor, how you choose to act and interact towards your 21 | fellow contributors, as well as to the community, will reflect back not only 22 | on yourself but on the project as a whole. The Code of Conduct is designed and 23 | intended, above all else, to help establish a culture within the project that 24 | allows anyone and everyone who wants to contribute to feel safe doing so. 25 | 26 | Should any individual act in any way that is considered in violation of the 27 | [Code of Conduct](./CODE_OF_CONDUCT.md), corrective actions will be taken. It is 28 | possible, however, for any individual to *act* in such a manner that is not in 29 | violation of the strict letter of the Code of Conduct guidelines while still 30 | going completely against the spirit of what that Code is intended to accomplish. 31 | 32 | Open, diverse, and inclusive communities live and die on the basis of trust. 33 | Contributors can disagree with one another so long as they trust that those 34 | disagreements are in good faith and everyone is working towards a common 35 | goal. 36 | 37 | ## Bad Actors 38 | All contributors to tacitly agree to abide by both the letter and 39 | spirit of the [Code of Conduct](./CODE_OF_CONDUCT.md). Failure, or 40 | unwillingness, to do so will result in contributions being respectfully 41 | declined. 42 | 43 | A *bad actor* is someone who repeatedly violates the *spirit* of the Code of 44 | Conduct through consistent failure to self-regulate the way in which they 45 | interact with other contributors in the project. In doing so, bad actors 46 | alienate other contributors, discourage collaboration, and generally reflect 47 | poorly on the project as a whole. 48 | 49 | Being a bad actor may be intentional or unintentional. Typically, unintentional 50 | bad behavior can be easily corrected by being quick to apologize and correct 51 | course *even if you are not entirely convinced you need to*. Giving other 52 | contributors the benefit of the doubt and having a sincere willingness to admit 53 | that you *might* be wrong is critical for any successful open collaboration. 54 | 55 | Don't be a bad actor. 56 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, 10 | education, socio-economic status, nationality, personal appearance, race, 11 | religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | - Using welcoming and inclusive language 19 | - Being respectful of differing viewpoints and experiences 20 | - Gracefully accepting constructive criticism 21 | - Focusing on what is best for the community 22 | - Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | - The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | - Trolling, insulting/derogatory comments, and personal or political attacks 29 | - Public or private harassment 30 | - Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | - Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of acceptable 39 | behavior and are expected to take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or 43 | reject comments, commits, code, wiki edits, issues, and other contributions 44 | that are not aligned to this Code of Conduct, or to ban temporarily or 45 | permanently any contributor for other behaviors that they deem inappropriate, 46 | threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies both within project spaces and in public spaces 51 | when an individual is representing the project or its community. Examples of 52 | representing a project or community include using an official project e-mail 53 | address, posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. Representation of a project may be 55 | further defined and clarified by project maintainers. 56 | 57 | ## Enforcement 58 | 59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 60 | reported by contacting the project team at conduct@yosh.is, or through 61 | IRC. All complaints will be reviewed and investigated and will result in a 62 | response that is deemed necessary and appropriate to the circumstances. The 63 | project team is obligated to maintain confidentiality with regard to the 64 | reporter of an incident. 65 | Further details of specific enforcement policies may be posted separately. 66 | 67 | Project maintainers who do not follow or enforce the Code of Conduct in good 68 | faith may face temporary or permanent repercussions as determined by other 69 | members of the project's leadership. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 74 | available at 75 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{quote, ToTokens}; 3 | use syn::{spanned::Spanned, Attribute, ExprPath, GenericParam, Path, Token}; 4 | 5 | /// Add the const param to the trait definition 6 | pub(crate) fn create_maybe_generics(generics: &syn::Generics) -> syn::Generics { 7 | let mut outer_generics = generics.clone(); 8 | let params = &mut outer_generics.params; 9 | if !params.empty_or_trailing() { 10 | params.push_punct(::default()); 11 | } 12 | let param = syn::parse2(quote! { const EMPLACE: bool = false }).unwrap(); 13 | params.push(GenericParam::Const(param)); 14 | outer_generics 15 | } 16 | 17 | /// Checks whether there is a `#[placing]` attribute in the list. 18 | /// 19 | /// # Errors 20 | /// 21 | /// This function will return an error if the attribute is malformed 22 | pub(crate) fn has_placing_attr(attrs: &[Attribute]) -> Result { 23 | for attr in attrs.iter() { 24 | if path_ident(attr.path()) != "placing" { 25 | continue; 26 | } 27 | 28 | return match &attr.meta { 29 | syn::Meta::Path(_) => Ok(true), 30 | _ => Err(quote::quote_spanned! { attr.span() => 31 | compile_error!("[E0004, placing] invalid attr: the #[placing] attribute does not support any additional arguments"), 32 | }.into()), 33 | }; 34 | } 35 | Ok(false) 36 | } 37 | 38 | /// Convert a path to its identity 39 | pub(crate) fn path_ident(path: &Path) -> String { 40 | path.to_token_stream().to_string() 41 | } 42 | 43 | /// Convert a path to its identity 44 | pub(crate) fn expr_path_ident(path: &ExprPath) -> String { 45 | path.to_token_stream().to_string() 46 | } 47 | 48 | pub(crate) fn strip_placing_attr(attrs: &mut Vec) { 49 | attrs.retain(|attr| path_ident(attr.path()) != "placing") 50 | } 51 | 52 | pub(crate) fn set_path_generics( 53 | path: &syn::TypePath, 54 | base: &syn::Generics, 55 | param: syn::GenericArgument, 56 | ) -> syn::TypePath { 57 | let mut path = path.clone(); 58 | let segment = path.path.segments.last_mut().unwrap(); 59 | let ident = &segment.ident; 60 | let params = base.params.iter().map(|param| -> syn::GenericArgument { 61 | match param { 62 | syn::GenericParam::Lifetime(lifetime_param) => { 63 | let param = &lifetime_param.lifetime; 64 | syn::parse2(quote! {#param}).unwrap() 65 | } 66 | syn::GenericParam::Type(type_param) => { 67 | let param = &type_param.ident; 68 | syn::parse2(quote! {#param}).unwrap() 69 | } 70 | syn::GenericParam::Const(const_param) => { 71 | let param = &const_param.ident; 72 | syn::parse2(quote! {#param}).unwrap() 73 | } 74 | } 75 | }); 76 | *segment = syn::parse2(quote! { #ident <#(#params,)* #param> }).unwrap(); 77 | path 78 | } 79 | 80 | /// Determine what kind of constructor a function is 81 | pub(crate) fn constructor_type(sig: &syn::Signature, ident: &syn::Ident) -> ConstructorKind { 82 | match &sig.output { 83 | syn::ReturnType::Type(_, ty) => match &**ty { 84 | syn::Type::Path(path) => match path_ident(&path.path).as_str() { 85 | "Box < Self >" => ConstructorKind::Pointer(PointerKind::Box), 86 | "Self" => ConstructorKind::Inline, 87 | s if s == ident.to_string() => ConstructorKind::Inline, 88 | _ => ConstructorKind::Other, 89 | }, 90 | _ => ConstructorKind::Other, 91 | }, 92 | _ => ConstructorKind::Other, 93 | } 94 | } 95 | 96 | /// Possible constructor kinds 97 | pub(crate) enum ConstructorKind { 98 | Inline, 99 | Pointer(PointerKind), 100 | Other, 101 | } 102 | 103 | pub(crate) enum PointerKind { 104 | Box, 105 | } 106 | -------------------------------------------------------------------------------- /src/inherent/placing/inline.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{spanned::Spanned, ImplItemFn}; 4 | 5 | use super::ImplFns; 6 | 7 | pub(crate) fn inline_constructor(output: &mut ImplFns, f: ImplItemFn) -> Result<(), TokenStream> { 8 | let fn_ident = f.sig.ident.clone(); 9 | 10 | // Create the new uninit constructor 11 | let syn::ImplItemFn { 12 | attrs, 13 | vis, 14 | defaultness: _, 15 | sig, 16 | mut block, 17 | } = f; 18 | 19 | let uninit = uninit_constructor(&fn_ident, &attrs, &vis); 20 | output.emplacing_constructors.push(uninit.into()); 21 | 22 | // Get the last expr in the function body 23 | let expr = match block.stmts.last_mut() { 24 | Some(syn::Stmt::Expr(expr, _)) => expr, 25 | Some(stmt) => return Err(quote::quote_spanned! { stmt.span() => 26 | compile_error!("[E0006, placing] invalid constructor body: functions marked `#[placing]` have to end with a constructor"), 27 | }.into()), 28 | None => return Err(quote::quote_spanned! { block.span() => 29 | compile_error!("[E0005, placing] empty constructor body: functions marked `#[placing]` cannot be empty"), 30 | }.into()), 31 | }; 32 | 33 | // Rewrite the last expr in the function body 34 | let syn::Expr::Struct(strukt) = expr else { 35 | return Err(quote::quote_spanned! { expr.span() => 36 | compile_error!("[E0006, placing] invalid constructor body: expected a struct expression as the last statement"), 37 | }.into()); 38 | }; 39 | *expr = inline_new_init(strukt); 40 | let constructor = init_constructor(fn_ident, sig, attrs, vis, block); 41 | output.emplacing_constructors.push(constructor.into()); 42 | 43 | Ok(()) 44 | } 45 | 46 | fn init_constructor( 47 | fn_ident: syn::Ident, 48 | sig: syn::Signature, 49 | attrs: Vec, 50 | vis: syn::Visibility, 51 | block: syn::Block, 52 | ) -> ImplItemFn { 53 | // Rewrite the existing constructors signature to emplace 54 | let init_ident = format_ident!("placing_init_{}", fn_ident); 55 | let inputs = sig.inputs.iter(); 56 | let statements = &block.stmts; 57 | 58 | let init: syn::ImplItemFn = syn::parse2(quote! { 59 | #(#attrs)* 60 | /// # Safety 61 | /// 62 | /// This method has been generated by the `placing` crate. 63 | /// It must be called using the `placing` constructor macro. 64 | #vis unsafe fn #init_ident(&mut self, #(#inputs,)*) 65 | { 66 | #(#statements)* 67 | } 68 | }) 69 | .unwrap(); 70 | init 71 | } 72 | 73 | fn uninit_constructor( 74 | fn_ident: &syn::Ident, 75 | attrs: &[syn::Attribute], 76 | vis: &syn::Visibility, 77 | ) -> ImplItemFn { 78 | let uninit_ident = format_ident!("placing_uninit_{}", fn_ident); 79 | let uninit: syn::ImplItemFn = syn::parse2(quote! { 80 | #(#attrs)* 81 | /// # Safety 82 | /// 83 | /// This method has been generated by the `placing` crate. 84 | /// It must be called using the `placing` constructor macro. 85 | #vis unsafe fn #uninit_ident() -> Self 86 | { 87 | Self { inner: ::core::mem::MaybeUninit::uninit() } 88 | } 89 | }) 90 | .unwrap(); 91 | uninit 92 | } 93 | 94 | /// Convert `Self { .. }` to writes into a `MaybeUninit` 95 | pub(crate) fn inline_new_init(strukt: &mut syn::ExprStruct) -> syn::Expr { 96 | let assignments = strukt 97 | .fields 98 | .iter() 99 | .map(|field| { 100 | let key = &field.member; 101 | let expr = &field.expr; 102 | syn::parse2(quote! {{ 103 | unsafe { (&raw mut (*_this).#key).write(#expr) }; 104 | }}) 105 | .unwrap() 106 | }) 107 | .collect::>(); 108 | 109 | syn::parse2(quote! {{ 110 | let _this = self.inner.as_mut_ptr(); 111 | #(#assignments)* 112 | }}) 113 | .unwrap() 114 | } 115 | -------------------------------------------------------------------------------- /src/inherent/placing/pointer.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{spanned::Spanned, ImplItemFn, Signature}; 4 | 5 | use super::ImplFns; 6 | use crate::utils::{expr_path_ident, PointerKind}; 7 | 8 | pub(crate) fn pointer_constructor( 9 | output: &mut ImplFns, 10 | f: ImplItemFn, 11 | kind: PointerKind, 12 | ) -> Result<(), TokenStream> { 13 | let fn_ident = f.sig.ident.clone(); 14 | 15 | // Create the new uninit constructor 16 | let syn::ImplItemFn { 17 | attrs, 18 | vis, 19 | defaultness: _, 20 | sig, 21 | mut block, 22 | } = f; 23 | 24 | // Construct the uninit function 25 | let uninit = uninit_constructor(&fn_ident, &attrs, &vis, kind); 26 | output.emplacing_constructors.push(uninit.into()); 27 | 28 | // Get the last expr in the function body 29 | let expr = match block.stmts.last_mut() { 30 | Some(syn::Stmt::Expr(expr, _)) => expr, 31 | Some(stmt) => return Err(quote::quote_spanned! { stmt.span() => 32 | compile_error!("[E0006, placing] invalid constructor body: functions marked `#[placing]` have to end with a constructor"), 33 | }.into()), 34 | None => return Err(quote::quote_spanned! { block.span() => 35 | compile_error!("[E0005, placing] empty constructor body: functions marked `#[placing]` cannot be empty"), 36 | }.into()), 37 | }; 38 | 39 | // Rewrite the last expr in the function body 40 | let syn::Expr::Call(call) = expr else { 41 | return Err(quote::quote_spanned! { expr.span() => 42 | compile_error!("[E0006, placing] invalid constructor body: expected a call expression as the last statement"), 43 | }.into()); 44 | }; 45 | *expr = pointer_new_init(call)?; 46 | let constructor = init_constructor(fn_ident, sig, attrs, vis, block); 47 | output.emplacing_constructors.push(constructor.into()); 48 | 49 | Ok(()) 50 | } 51 | 52 | fn init_constructor( 53 | fn_ident: syn::Ident, 54 | sig: Signature, 55 | attrs: Vec, 56 | vis: syn::Visibility, 57 | block: syn::Block, 58 | ) -> ImplItemFn { 59 | // Rewrite the existing constructors signature to emplace 60 | let init_ident = format_ident!("placing_init_{}", fn_ident); 61 | let inputs = sig.inputs.iter(); 62 | let statements = &block.stmts; 63 | 64 | let init: syn::ImplItemFn = syn::parse2(quote! { 65 | #(#attrs)* 66 | /// # Safety 67 | /// 68 | /// This method has been generated by the `placing` crate. 69 | /// It must be called using the `placing` constructor macro. 70 | #vis unsafe fn #init_ident(self: &mut Box, #(#inputs,)*) 71 | { 72 | #(#statements)* 73 | } 74 | }) 75 | .unwrap(); 76 | init 77 | } 78 | 79 | fn uninit_constructor( 80 | fn_ident: &syn::Ident, 81 | attrs: &[syn::Attribute], 82 | vis: &syn::Visibility, 83 | _kind: PointerKind, 84 | ) -> ImplItemFn { 85 | let uninit_ident = format_ident!("placing_uninit_{}", fn_ident); 86 | 87 | let uninit: syn::ImplItemFn = syn::parse2(quote! { 88 | #(#attrs)* 89 | /// # Safety 90 | /// 91 | /// This method has been generated by the `placing` crate. 92 | /// It must be called using the `placing` constructor macro. 93 | #vis unsafe fn #uninit_ident() -> Box 94 | { 95 | Box::new(Self { inner: ::core::mem::MaybeUninit::uninit() }) 96 | } 97 | }) 98 | .unwrap(); 99 | uninit 100 | } 101 | 102 | /// Convert `Box::new(Self { .. })` to writes into a `Box>` 103 | pub(crate) fn pointer_new_init(call: &mut syn::ExprCall) -> Result { 104 | let syn::Expr::Path(path) = &*call.func else { 105 | return Err(quote::quote_spanned! { call.span() => 106 | compile_error!("[E0006, placing] invalid constructor body: functions marked `#[placing]` can only end with a fixed set of expressions"), 107 | }.into()); 108 | }; 109 | 110 | match expr_path_ident(path).as_str() { 111 | "Box :: new" => {} 112 | _ => { 113 | return Err(quote::quote_spanned! { call.span() => 114 | compile_error!("[E0008, placing] invalid pointer constructor"), 115 | } 116 | .into()) 117 | } 118 | } 119 | 120 | let strukt = match call.args.first() { 121 | Some(syn::Expr::Struct(expr)) => expr, 122 | _ => { 123 | return Err(quote::quote_spanned! { call.span() => 124 | compile_error!("[E0008, placing] invalid pointer constructor`"), 125 | } 126 | .into()) 127 | } 128 | }; 129 | 130 | let assignments = strukt 131 | .fields 132 | .iter() 133 | .map(|field| { 134 | let key = &field.member; 135 | let expr = &field.expr; 136 | syn::parse2(quote! {{ 137 | unsafe { (&raw mut (*_this).#key).write(#expr) }; 138 | }}) 139 | .unwrap() 140 | }) 141 | .collect::>(); 142 | 143 | Ok(syn::parse2(quote! {{ 144 | let _this = self.inner.as_mut_ptr(); 145 | #(#assignments)* 146 | }}) 147 | .unwrap()) 148 | } 149 | -------------------------------------------------------------------------------- /src/inherent/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | use quote::quote; 4 | use syn::{spanned::Spanned, ImplItem, ImplItemFn, ItemImpl}; 5 | 6 | use crate::utils::{ 7 | create_maybe_generics, has_placing_attr, set_path_generics, strip_placing_attr, 8 | }; 9 | 10 | mod fn_kind; 11 | mod moving; 12 | mod placing; 13 | 14 | /// Process an impl block that carries the `#[placing]` notation 15 | pub(crate) fn process_impl(item: ItemImpl) -> TokenStream { 16 | if item.trait_.is_some() { 17 | return quote::quote! { 18 | compile_error!("`[placing, E0002] trait impls unsupported: macro `placing` can only be used on bare `impl {}` blocks"); 19 | }.into(); 20 | } 21 | 22 | // We need all the impl components to later recreate it 23 | // and fill it with our own methods 24 | let ItemImpl { 25 | attrs: _, 26 | defaultness, 27 | unsafety, 28 | impl_token, 29 | generics, 30 | trait_: _, 31 | self_ty, 32 | brace_token: _, 33 | items, 34 | } = item; 35 | 36 | // Validate we're processing an impl we know how to handle 37 | let self_ty = match *self_ty { 38 | syn::Type::Path(type_path) => type_path, 39 | _ => return quote::quote_spanned! { impl_token.span() => 40 | compile_error!("[E0003, placing] invalid impl target: `placing` doesn't work for impls on tuples, slices, or other non-path types"), 41 | }.into(), 42 | }; 43 | let self_ident = &self_ty.path.segments.last().unwrap().ident.clone(); 44 | 45 | // We need to generate three different impl blocks: 46 | // - one where EMPLACE is true 47 | // - one where EMPLACE is false 48 | // - one where EMPLACE is generic 49 | let self_ty_true = set_path_generics(&self_ty, &generics, syn::parse2(quote! {true}).unwrap()); 50 | let self_ty_false = 51 | set_path_generics(&self_ty, &generics, syn::parse2(quote! {false}).unwrap()); 52 | let self_ty_maybe = 53 | set_path_generics(&self_ty, &generics, syn::parse2(quote! {EMPLACE}).unwrap()); 54 | 55 | // Create our final sets of generic params 56 | let (impl_generics, _, where_clause) = generics.split_for_impl(); 57 | let maybe_generics = create_maybe_generics(&generics); 58 | let (impl_generics_maybe, _, where_clause_maybe) = maybe_generics.split_for_impl(); 59 | 60 | // We only want to modify the methods, the rest of the items we're happy to 61 | // pass along as-is. 62 | let mut fn_items = vec![]; 63 | let mut non_fn_items = vec![]; 64 | for item in items { 65 | match item { 66 | ImplItem::Fn(f) => fn_items.push(f), 67 | item => non_fn_items.push(item), 68 | } 69 | } 70 | let fns = match rewrite_fns(fn_items, &self_ident) { 71 | Ok(fn_items) => fn_items, 72 | Err(token_stream) => return token_stream, 73 | }; 74 | 75 | let ImplFns { 76 | statics, 77 | methods, 78 | emplacing_constructors, 79 | non_emplacing_constructors, 80 | } = fns; 81 | 82 | // All done now, send back our updated `impl` block 83 | quote! { 84 | #defaultness #unsafety #impl_token #impl_generics #self_ty_false #where_clause { 85 | #(#non_emplacing_constructors)* 86 | #(#non_fn_items)* 87 | #(#statics)* 88 | } 89 | #defaultness #unsafety #impl_token #impl_generics #self_ty_true #where_clause { 90 | #(#emplacing_constructors)* 91 | } 92 | #defaultness #unsafety #impl_token #impl_generics_maybe #self_ty_maybe #where_clause_maybe { 93 | #(#methods)* 94 | } 95 | } 96 | .into() 97 | } 98 | 99 | /// The output of `rewrite_fns` 100 | #[derive(Default)] 101 | struct ImplFns { 102 | statics: Vec, 103 | methods: Vec, 104 | emplacing_constructors: Vec, 105 | non_emplacing_constructors: Vec, 106 | } 107 | 108 | /// Process thef functions one by one 109 | fn rewrite_fns(fn_items: Vec, self_ident: &syn::Ident) -> Result { 110 | let mut output = ImplFns::default(); 111 | for mut f in fn_items { 112 | // Process and identify the function 113 | let fn_kind = fn_kind::FunctionKind::from_fn(&f.sig, self_ident); 114 | let has_placing = has_placing_attr(&f.attrs)?; 115 | strip_placing_attr(&mut f.attrs); 116 | 117 | // Validate the function bodies and rewrite them where needed. 118 | match (&fn_kind, has_placing) { 119 | (fn_kind::FunctionKind::Static, false) => { 120 | output.statics.push(f.into()); 121 | } 122 | (fn_kind::FunctionKind::Method, false) => { 123 | output.methods.push(f.into()); 124 | } 125 | (fn_kind::FunctionKind::Static, true) => { 126 | return Err(quote::quote_spanned! { f.sig.span() => 127 | compile_error!("[E0007, placing] invalid placing target: the #[placing] attribute cannot be applied to static functions"), 128 | }.into()); 129 | } 130 | (fn_kind::FunctionKind::Method, true) => { 131 | return Err(quote::quote_spanned! { f.sig.span() => 132 | compile_error!("[E0007, placing] invalid placing target: the #[placing] attribute cannot be applied to static functions"), 133 | }.into()); 134 | } 135 | (fn_kind::FunctionKind::Constructor(_heap_ty), false) => { 136 | moving::rewrite_moving_constructor(&mut output, f, self_ident)?; 137 | } 138 | (fn_kind::FunctionKind::Constructor(_heap_ty), true) => { 139 | placing::rewrite_placing_constructor(&mut output, f.clone(), self_ident)?; 140 | // TODO: re-enable me 141 | // moving_constructor::rewrite_moving_constructor(&mut output, f, self_ident)?; 142 | } 143 | (fn_kind::FunctionKind::Builder(_heap_ty), true) => { 144 | todo!("builders and transforms not yet supported") 145 | } 146 | (fn_kind::FunctionKind::Builder(_heap_ty), false) => { 147 | todo!("builders and transforms not yet supported") 148 | } 149 | } 150 | } 151 | Ok(output) 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

placing

2 |
3 | 4 | A prototype notation for referentially stable constructors 5 | 6 |
7 | 8 |
9 | 10 |
11 | 12 | 13 | Crates.io version 15 | 16 | 17 | 18 | Download 20 | 21 | 22 | 23 | docs.rs docs 25 | 26 |
27 | 28 | 43 | 44 | ## Installation 45 | ```sh 46 | $ cargo add placing 47 | ``` 48 | 49 | ## Example 50 | 51 | This crate enables address-sensitive types to be constructed. That is: types 52 | whose address in memory can't change over time. As well as types that might OOM 53 | if they are constructed on the stack before being copied to the heap. To start 54 | we create a new type with the `placing` attribute macro. This sets up the right 55 | internal type hierarchy for us. 56 | 57 | ```rust 58 | #[placing::placing] 59 | struct Cat { 60 | age: u8, 61 | } 62 | ``` 63 | 64 | We can then define our constructors and other methods. Constructors need to end 65 | with a struct expression as the last statement in the block, and need to be 66 | annotated with the `#[placing]` attribute. This allows us to transform it to be 67 | constructed directly in the caller's stack frame. Getters and other methods have 68 | no restrictions and can be freely used. 69 | 70 | ```rust 71 | #[placing::placing] 72 | impl Cat { 73 | /// Construct a new instance of `Cat` in-place 74 | #[placing] 75 | fn new(age: u8) -> Self { 76 | Self { age } 77 | } 78 | 79 | /// Returns the age of the cat 80 | fn age(&self) -> &u8 { 81 | &self.age 82 | } 83 | } 84 | ``` 85 | 86 | Finally it's time to create an instance of our type. This is the most 87 | scary-looking part of this crate, but once you understand what's going on it's 88 | fairly straightforward. Earlier we defined our `Cat::new` constructor. The 89 | placing crate has broken this up into two parts: `new_uninit` which creates the 90 | "place". And `new_init` which instantiates the type in the place. All you have 91 | to do is call both of these right after each other, and voila - in-place 92 | construction! 93 | 94 | ```rust 95 | fn main() { 96 | // Create the place for `cat` 97 | let mut cat = unsafe { Cat::new_uninit() }; 98 | // Instantiate the fields on `cat` 99 | unsafe { cat.new_init(12) }; 100 | // Type can now be used as normal 101 | assert_eq!(cat.age(), &12); 102 | } 103 | ``` 104 | 105 | To define a constructor which places a type directly on the heap rather than on 106 | the stack, just write `-> Box<_>` and `Box::new` and 107 | `placing` will take care of the rest. 108 | 109 | ```rust 110 | #[placing::placing] 111 | impl Cat { 112 | /// Construct a new instance of `Cat` in-place on the heap 113 | #[placing] 114 | fn new(age: u8) -> Box { 115 | Box::new(Self { age }) 116 | } 117 | } 118 | ``` 119 | 120 | ## The language feature 121 | 122 | This crate uses proc macros to prototype a new language feature. Proc macros are 123 | less powerful than what a compiler can do, as it can only provide limited 124 | syntactic transforms and does not have access to semantic analysis. Because of 125 | this a language feature should become a lot more streamlined. Assuming we'd have 126 | some first-class `placing` notation, the compiler would allow us to write our 127 | earlier example as follows: 128 | 129 | ```rust 130 | struct Cat { 131 | age: u8, 132 | } 133 | 134 | impl Cat { 135 | placing fn new(age: u8) -> Self { 136 | Self { age } 137 | } 138 | 139 | fn age(&self) -> &u8 { 140 | &self.age 141 | } 142 | } 143 | 144 | fn main() { 145 | let cat = Cat::new(12); 146 | assert_eq!(cat.age(), &12); 147 | } 148 | ``` 149 | 150 | That's just a single extra annotation on the constructor. Everything else 151 | continues working exactly the same, which is what makes this feature so 152 | appealing. 153 | 154 | ## Safety 155 | This crate prototypes a new language feature and liberally makes use of `unsafe`. 156 | 157 | ## Contributing 158 | Want to join us? Check out our ["Contributing" guide][contributing] and take a 159 | look at some of these issues: 160 | 161 | - [Issues labeled "good first issue"][good-first-issue] 162 | - [Issues labeled "help wanted"][help-wanted] 163 | 164 | [contributing]: https://github.com/yoshuawuyts/placing/blob/main/.github/CONTRIBUTING.md 165 | [good-first-issue]: https://github.com/yoshuawuyts/placing/labels/good%20first%20issue 166 | [help-wanted]: https://github.com/yoshuawuyts/placing/labels/help%20wanted 167 | 168 | ## See Also 169 | 170 | - [rust-for-linux/pinned-init](https://github.com/Rust-for-Linux/pinned-init) 171 | 172 | ## References 173 | 174 | - [The safe pinned initialization problem - Rust for Linux](https://rust-for-linux.com/the-safe-pinned-initialization-problem) 175 | - [Rust Temporary Lifetimes and "Super Let" - Mara Bos](https://blog.m-ou.se/super-let/) 176 | - [In-place construction seems surprisingly simple? - Yosh Wuyts](https://blog.yoshuawuyts.com/in-place-construction-seems-surprisingly-simple/) 177 | - [Ergonomic self-referential types for Rust - Yosh Wuyts](https://blog.yoshuawuyts.com/self-referential-types/) 178 | 179 | ## License 180 | 181 | 182 | Licensed under either of Apache License, Version 183 | 2.0 or MIT license at your option. 184 | 185 | 186 |
187 | 188 | 189 | Unless you explicitly state otherwise, any contribution intentionally submitted 190 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 191 | be dual licensed as above, without any additional terms or conditions. 192 | 193 | -------------------------------------------------------------------------------- /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 | Copyright 2020 Yoshua Wuyts 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | --------------------------------------------------------------------------------