Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 21 additions & 27 deletions internal/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use quote::{format_ident, quote, quote_spanned, ToTokens, TokenStreamExt};
use syn::{
braced, parenthesized,
parse::{End, Parse},
Expand Down Expand Up @@ -269,7 +269,6 @@ fn expand(
},
|(_, err)| Box::new(err),
);
let slot = format_ident!("slot");
let (has_data_trait, get_data, init_from_closure) = if pinned {
(
format_ident!("HasPinData"),
Expand All @@ -286,7 +285,7 @@ fn expand(
let init_kind = get_init_kind(rest, dcx);
let zeroable_check = match init_kind {
InitKind::Normal => quote!(),
InitKind::Zeroing => quote! {
InitKind::Zeroing => quote_spanned! { Span::mixed_site() =>
// The user specified `..Zeroable::zeroed()` at the end of the list of fields.
// Therefore we check if the struct implements `Zeroable` and then zero the memory.
// This allows us to also remove the check that all fields are present (since we
Expand All @@ -295,34 +294,33 @@ fn expand(
where T: ::pin_init::Zeroable
{}
// Ensure that the struct is indeed `Zeroable`.
assert_zeroable(#slot);
assert_zeroable(slot);
// SAFETY: The type implements `Zeroable` by the check above.
unsafe { ::core::ptr::write_bytes(#slot, 0, 1) };
unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
},
};
let this = match this {
None => quote!(),
Some(This { ident, .. }) => quote! {
Some(This { ident, .. }) => quote_spanned! { Span::mixed_site() =>
// Create the `this` so it can be referenced by the user inside of the
// expressions creating the individual fields.
let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
},
};
// `mixed_site` ensures that the data is not accessible to the user-controlled code.
let data = Ident::new("__data", Span::mixed_site());
let init_fields = init_fields(&fields, pinned, &data, &slot);
let init_fields = init_fields(&fields, pinned);
let field_check = make_field_check(&fields, init_kind, &path);
Ok(quote! {{
Ok(quote_spanned! { Span::mixed_site() => {
// Get the data about fields from the supplied type.
// SAFETY: TODO
let #data = unsafe {
let data = unsafe {
use ::pin_init::__internal::#has_data_trait;
// Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit
// generics (which need to be present with that syntax).
#path::#get_data()
};
// Ensure that `#data` really is of type `#data` and help with type inference:
let init = #data.__make_closure::<_, #error>(
// Ensure that `data` really is of type `data` and help with type inference:
let init = data.__make_closure::<_, #error>(
move |slot| {
#zeroable_check
#this
Expand Down Expand Up @@ -380,12 +378,7 @@ fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKi
}

/// Generate the code that initializes the fields of the struct using the initializers in `field`.
fn init_fields(
fields: &Punctuated<InitializerField, Token![,]>,
pinned: bool,
data: &Ident,
slot: &Ident,
) -> TokenStream {
fn init_fields(fields: &Punctuated<InitializerField, Token![,]>, pinned: bool) -> TokenStream {
let mut guards = vec![];
let mut guard_attrs = vec![];
let mut res = TokenStream::new();
Expand All @@ -411,18 +404,19 @@ fn init_fields(
}
};
let ident = member.as_ident();
let span = Span::mixed_site().located_at(ident.span());

let slot = if pinned {
quote! {
quote_spanned! { span =>
// SAFETY:
// - `slot` is valid and properly aligned.
// - `make_field_check` checks that `&raw mut (*slot).#member` is properly aligned.
// - `make_field_check` prevents `#member` from being used twice, therefore
// `(*slot).#member` is exclusively accessed and has not been initialized.
(unsafe { #data.#ident(#slot) })
(unsafe { data.#ident(slot) })
}
} else {
quote! {
quote_spanned! { span =>
// For `init!()` macro, everything is unpinned.
// SAFETY:
// - `&raw mut (*slot).#member` is valid.
Expand All @@ -431,14 +425,15 @@ fn init_fields(
// `(*slot).#member` is exclusively accessed and has not been initialized.
(unsafe {
::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
&raw mut (*#slot).#member
&raw mut (*slot).#member
)
})
}
};

// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
let full_span = kind.span();

let init = match kind {
InitializerKind::Value { value, .. } => {
Expand All @@ -447,14 +442,13 @@ fn init_fields(
.map(|(_, value)| quote!(#value))
.unwrap_or_else(|| quote!(#member));

quote! {
quote_spanned! { full_span =>
#(#attrs)*
let mut #guard = #slot.write(#value);

}
}
InitializerKind::Init { value, .. } => {
quote! {
quote_spanned! { full_span =>
#(#attrs)*
let mut #guard = #slot.init(#value)?;
}
Expand All @@ -465,7 +459,7 @@ fn init_fields(
// A tuple field has no name that could be bound here (the `_0` identifiers are considered
// implementation detail and not user-facing).
let binding = match member {
Member::Named(ident) => quote! {
Member::Named(ident) => quote_spanned! { span =>
#(#cfgs)*
// Allow `non_snake_case` since the same warning is going to be reported for the
// struct field.
Expand Down Expand Up @@ -512,7 +506,7 @@ fn make_field_check(
..::core::mem::zeroed()
}),
};
quote! {
quote_spanned! { Span::mixed_site() =>
#[allow(unreachable_code)]
// We use unreachable code to perform field checks. They're still checked by the compiler.
// SAFETY: this code is never executed.
Expand Down
2 changes: 1 addition & 1 deletion internal/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl MemberExt for Member {
fn as_ident(&self) -> Ident {
match self {
Member::Named(ident) => ident.clone(),
Member::Unnamed(Index { index, .. }) => format_ident!("_{index}"),
Member::Unnamed(Index { index, span }) => format_ident!("_{index}", span = *span),
}
}

Expand Down
9 changes: 9 additions & 0 deletions tests/macro-hygiene.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use pin_init::*;

macro_rules! wrap_init {
($($args:tt)*) => {
::pin_init::init!(
Expand All @@ -12,11 +14,18 @@ struct Foo {
c: u32,
}

#[pin_data]
struct SlotName {
slot: u32,
}

fn main() {
let c = 3;
let _ = wrap_init!(Foo {
a: 1,
b <- 2,
c,
});

let _ = pin_init!(SlotName { slot: 1 });
}
14 changes: 6 additions & 8 deletions tests/ui/compile-fail/init/colon_instead_of_arrow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@ error[E0308]: mismatched types
| ------------------ the found opaque type
...
21 | pin_init!(Self { bar: Bar::new() })
| ----------------------^^^^^^^^^^---
| | |
| | expected `Bar`, found opaque type
| arguments to this method are incorrect
| -----^^^^^^^^^^
| | |
| | expected `Bar`, found opaque type
| arguments to this method are incorrect
|
= note: expected struct `Bar`
found opaque type `impl pin_init::PinInit<Bar>`
help: the return type of this call is `impl pin_init::PinInit<Bar>` due to the type of the argument passed
--> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:9
--> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:26
|
21 | pin_init!(Self { bar: Bar::new() })
| ^^^^^^^^^^^^^^^^^^^^^^----------^^^
| |
| this argument influences the return type of `write`
| ^^^ ---------- this argument influences the return type of `write`
note: method defined here
--> src/__internal.rs
|
Expand Down
20 changes: 4 additions & 16 deletions tests/ui/compile-fail/init/data_access.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
error[E0425]: cannot find value `__data` in this scope
--> tests/ui/compile-fail/init/data_access.rs:9:21
|
9 | let _ = __data;
| ^^^^^^ not found in this scope
|
help: an identifier with the same name is defined here, but is not accessible due to macro hygiene
--> tests/ui/compile-fail/init/data_access.rs:7:13
|
7 | let _ = pin_init!(Foo {
| _____________^
8 | | _: {
9 | | let _ = __data;
10 | | },
11 | | });
| |______^
= note: this error originates in the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info)
--> tests/ui/compile-fail/init/data_access.rs:9:21
|
9 | let _ = __data;
| ^^^^^^ not found in this scope
17 changes: 6 additions & 11 deletions tests/ui/compile-fail/init/early_return.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ note: tuple variant defined here
| ^^

warning: unreachable statement
--> tests/ui/compile-fail/init/early_return.rs:8:13
--> tests/ui/compile-fail/init/early_return.rs:12:9
|
8 | let _ = init!(Foo {
| _____________^
9 | | _: {
10 | | return Ok(());
| | ------------- any code following this expression is unreachable
11 | | },
12 | | a: 42,
13 | | });
| |______^ unreachable statement
10 | return Ok(());
| ------------- any code following this expression is unreachable
11 | },
12 | a: 42,
| ^^^^^ unreachable statement
|
= note: `#[warn(unreachable_code)]` (part of `#[warn(unused)]`) on by default
= note: this warning originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
14 changes: 6 additions & 8 deletions tests/ui/compile-fail/init/field_value_wrong_type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ error[E0308]: mismatched types
--> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:28
|
8 | let _ = init!(Foo { a: () });
| ---------------^^---
| | |
| | expected `usize`, found `()`
| arguments to this method are incorrect
| ---^^
| | |
| | expected `usize`, found `()`
| arguments to this method are incorrect
|
help: the return type of this call is `()` due to the type of the argument passed
--> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:13
--> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:25
|
8 | let _ = init!(Foo { a: () });
| ^^^^^^^^^^^^^^^--^^^
| |
| this argument influences the return type of `write`
| ^ -- this argument influences the return type of `write`
note: method defined here
--> src/__internal.rs
|
Expand Down
11 changes: 5 additions & 6 deletions tests/ui/compile-fail/init/invalid_init.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
error[E0277]: the trait bound `impl pin_init::PinInit<Bar>: Init<Bar, _>` is not satisfied
--> tests/ui/compile-fail/init/invalid_init.rs:19:16
|
18 | let _ = init!(Foo {
| _____________-
19 | | bar <- Bar::new(),
| | ^^^^^^^^^^ the trait `Init<Bar, _>` is not implemented for `impl pin_init::PinInit<Bar>`
20 | | });
| |______- required by a bound introduced by this call
19 | bar <- Bar::new(),
| -------^^^^^^^^^^
| | |
| | the trait `Init<Bar, _>` is not implemented for `impl pin_init::PinInit<Bar>`
| required by a bound introduced by this call
|
help: the following other types implement trait `Init<T, E>`
--> src/lib.rs
Expand Down
21 changes: 8 additions & 13 deletions tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
error[E0308]: mismatched types
--> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:12:12
|
11 | let _ = init!(Foo {
| _____________-
12 | | a: 0..Zeroable::init_zeroed()
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Range<{integer}>`
13 | | });
| |______- arguments to this method are incorrect
12 | a: 0..Zeroable::init_zeroed()
| ---^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | expected `usize`, found `Range<{integer}>`
| arguments to this method are incorrect
|
= note: expected type `usize`
found struct `std::ops::Range<{integer}>`
help: the return type of this call is `std::ops::Range<{integer}>` due to the type of the argument passed
--> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:11:13
--> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:12:9
|
11 | let _ = init!(Foo {
| _____________^
12 | | a: 0..Zeroable::init_zeroed()
| | -------------------------- this argument influences the return type of `write`
13 | | });
| |______^
12 | a: 0..Zeroable::init_zeroed()
| ^ -------------------------- this argument influences the return type of `write`
note: method defined here
--> src/__internal.rs
|
Expand Down
15 changes: 6 additions & 9 deletions tests/ui/compile-fail/init/no_error_coercion.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
error[E0277]: `?` couldn't convert the error to `std::alloc::AllocError`
--> tests/ui/compile-fail/init/no_error_coercion.rs:19:22
--> tests/ui/compile-fail/init/no_error_coercion.rs:18:15
|
16 | / init!(Self {
17 | | a: Box::new(42),
18 | | bar <- init!(Bar { b: 42 }),
19 | | }? AllocError)
| | ^
| | |
| |______________________the trait `From<!>` is not implemented for `std::alloc::AllocError`
| this can't be annotated with `?` because it has type `Result<_, !>`
18 | bar <- init!(Bar { b: 42 }),
| --^------------------------
| | |
| | the trait `From<!>` is not implemented for `std::alloc::AllocError`
| this can't be annotated with `?` because it has type `Result<_, !>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
14 changes: 6 additions & 8 deletions tests/ui/compile-fail/init/shadowing_field.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ error[E0308]: mismatched types
--> tests/ui/compile-fail/init/shadowing_field.rs:10:31
|
10 | let _ = init!(Foo { x, y: x });
| ------------------^---
| | |
| | expected `usize`, found `&mut usize`
| arguments to this method are incorrect
| ---^
| | |
| | expected `usize`, found `&mut usize`
| arguments to this method are incorrect
|
help: the return type of this call is `&mut usize` due to the type of the argument passed
--> tests/ui/compile-fail/init/shadowing_field.rs:10:13
--> tests/ui/compile-fail/init/shadowing_field.rs:10:28
|
10 | let _ = init!(Foo { x, y: x });
| ^^^^^^^^^^^^^^^^^^-^^^
| |
| this argument influences the return type of `write`
| ^ - this argument influences the return type of `write`
note: method defined here
--> src/__internal.rs
|
Expand Down
Loading