diff --git a/internal/src/init.rs b/internal/src/init.rs index dc5dfb2c..1d2db93d 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -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}, @@ -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"), @@ -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 @@ -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 @@ -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, - pinned: bool, - data: &Ident, - slot: &Ident, -) -> TokenStream { +fn init_fields(fields: &Punctuated, pinned: bool) -> TokenStream { let mut guards = vec![]; let mut guard_attrs = vec![]; let mut res = TokenStream::new(); @@ -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. @@ -431,7 +425,7 @@ 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 ) }) } @@ -439,6 +433,7 @@ fn init_fields( // `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, .. } => { @@ -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)?; } @@ -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. @@ -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. diff --git a/internal/src/util.rs b/internal/src/util.rs index ed2c78f0..0b5c28b2 100644 --- a/internal/src/util.rs +++ b/internal/src/util.rs @@ -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), } } diff --git a/tests/macro-hygiene.rs b/tests/macro-hygiene.rs index 1258adee..e7b28fe8 100644 --- a/tests/macro-hygiene.rs +++ b/tests/macro-hygiene.rs @@ -1,3 +1,5 @@ +use pin_init::*; + macro_rules! wrap_init { ($($args:tt)*) => { ::pin_init::init!( @@ -12,6 +14,11 @@ struct Foo { c: u32, } +#[pin_data] +struct SlotName { + slot: u32, +} + fn main() { let c = 3; let _ = wrap_init!(Foo { @@ -19,4 +26,6 @@ fn main() { b <- 2, c, }); + + let _ = pin_init!(SlotName { slot: 1 }); } diff --git a/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr b/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr index bc6ee146..0847e9dd 100644 --- a/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr +++ b/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr @@ -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` help: the return type of this call is `impl pin_init::PinInit` 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 | diff --git a/tests/ui/compile-fail/init/data_access.stderr b/tests/ui/compile-fail/init/data_access.stderr index ef5f3d23..c14114b4 100644 --- a/tests/ui/compile-fail/init/data_access.stderr +++ b/tests/ui/compile-fail/init/data_access.stderr @@ -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 diff --git a/tests/ui/compile-fail/init/early_return.stderr b/tests/ui/compile-fail/init/early_return.stderr index 82d56a08..e9e2ba98 100644 --- a/tests/ui/compile-fail/init/early_return.stderr +++ b/tests/ui/compile-fail/init/early_return.stderr @@ -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) diff --git a/tests/ui/compile-fail/init/field_value_wrong_type.stderr b/tests/ui/compile-fail/init/field_value_wrong_type.stderr index d506bf0b..7fa67217 100644 --- a/tests/ui/compile-fail/init/field_value_wrong_type.stderr +++ b/tests/ui/compile-fail/init/field_value_wrong_type.stderr @@ -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 | diff --git a/tests/ui/compile-fail/init/invalid_init.stderr b/tests/ui/compile-fail/init/invalid_init.stderr index 843710a5..c0f10d9b 100644 --- a/tests/ui/compile-fail/init/invalid_init.stderr +++ b/tests/ui/compile-fail/init/invalid_init.stderr @@ -1,12 +1,11 @@ error[E0277]: the trait bound `impl pin_init::PinInit: Init` is not satisfied --> tests/ui/compile-fail/init/invalid_init.rs:19:16 | -18 | let _ = init!(Foo { - | _____________- -19 | | bar <- Bar::new(), - | | ^^^^^^^^^^ the trait `Init` is not implemented for `impl pin_init::PinInit` -20 | | }); - | |______- required by a bound introduced by this call +19 | bar <- Bar::new(), + | -------^^^^^^^^^^ + | | | + | | the trait `Init` is not implemented for `impl pin_init::PinInit` + | required by a bound introduced by this call | help: the following other types implement trait `Init` --> src/lib.rs diff --git a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr index a2978266..b8eec061 100644 --- a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr +++ b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr @@ -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 | diff --git a/tests/ui/compile-fail/init/no_error_coercion.stderr b/tests/ui/compile-fail/init/no_error_coercion.stderr index ddc13df7..5092e295 100644 --- a/tests/ui/compile-fail/init/no_error_coercion.stderr +++ b/tests/ui/compile-fail/init/no_error_coercion.stderr @@ -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) diff --git a/tests/ui/compile-fail/init/shadowing_field.stderr b/tests/ui/compile-fail/init/shadowing_field.stderr index af21f62a..5e1e038f 100644 --- a/tests/ui/compile-fail/init/shadowing_field.stderr +++ b/tests/ui/compile-fail/init/shadowing_field.stderr @@ -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 | diff --git a/tests/ui/compile-fail/init/tuple_invalid_field.stderr b/tests/ui/compile-fail/init/tuple_invalid_field.stderr index f18d6e88..9ed771c6 100644 --- a/tests/ui/compile-fail/init/tuple_invalid_field.stderr +++ b/tests/ui/compile-fail/init/tuple_invalid_field.stderr @@ -1,13 +1,17 @@ error[E0599]: no method named `_2` found for struct `__ThePinData` in the current scope - --> tests/ui/compile-fail/init/tuple_invalid_field.rs:7:13 + --> tests/ui/compile-fail/init/tuple_invalid_field.rs:7:43 | 3 | #[pin_data] | ----------- method `_2` not found for this struct ... 7 | let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ + | +help: there is a method `_0` with a similar name + | +7 - let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 }); +7 + let _ = pin_init!(Tuple { 0: 1, 1: 2, _0: 3 }); | - = note: this error originates in the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0609]: no field `2` on type `Tuple` --> tests/ui/compile-fail/init/tuple_invalid_field.rs:7:43 diff --git a/tests/ui/compile-fail/pin_data/missing_pin.stderr b/tests/ui/compile-fail/pin_data/missing_pin.stderr index fa39129f..b8ad1503 100644 --- a/tests/ui/compile-fail/pin_data/missing_pin.stderr +++ b/tests/ui/compile-fail/pin_data/missing_pin.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `impl PinInit: Init` is not satisfied --> tests/ui/compile-fail/pin_data/missing_pin.rs:12:18 | -11 | / pin_init!(Self { -12 | | a <- a, - | | ^ the trait `Init` is not implemented for `impl PinInit` -13 | | }) - | |__________- required by a bound introduced by this call +12 | a <- a, + | -----^ + | | | + | | the trait `Init` is not implemented for `impl PinInit` + | required by a bound introduced by this call | help: the trait `Init` is not implemented for `impl PinInit` but trait `Init, !>` is implemented for it diff --git a/tests/ui/expand/simple-init.expanded.rs b/tests/ui/expand/simple-init.expanded.rs index d001aa69..0dfc1cb4 100644 --- a/tests/ui/expand/simple-init.expanded.rs +++ b/tests/ui/expand/simple-init.expanded.rs @@ -2,11 +2,11 @@ use pin_init::*; struct Foo {} fn main() { let _ = { - let __data = unsafe { + let data = unsafe { use ::pin_init::__internal::HasInitData; Foo::__init_data() }; - let init = __data + let init = data .__make_closure::< _, ::core::convert::Infallible, diff --git a/tests/ui/expand/tuple_struct.expanded.rs b/tests/ui/expand/tuple_struct.expanded.rs index 81287de7..61aefecc 100644 --- a/tests/ui/expand/tuple_struct.expanded.rs +++ b/tests/ui/expand/tuple_struct.expanded.rs @@ -142,11 +142,11 @@ const _: () = { fn main() { let mut first = [1u8, 2, 3]; let _ = { - let __data = unsafe { + let data = unsafe { use ::pin_init::__internal::HasInitData; Foo::__init_data() }; - let init = __data + let init = data .__make_closure::< _, ::core::convert::Infallible, @@ -200,11 +200,11 @@ fn main() { }; let mut second = [4u8, 5, 6]; let _ = { - let __data = unsafe { + let data = unsafe { use ::pin_init::__internal::HasInitData; Foo::__init_data() }; - let init = __data + let init = data .__make_closure::< _, ::core::convert::Infallible,