Received: by 2002:a05:7412:6592:b0:d7:7d3a:4fe2 with SMTP id m18csp2441978rdg; Mon, 14 Aug 2023 02:37:58 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGgBd68O5f8crZ3GH3iPgU9C2YsyPCeCieSgnzTMnfERUVbw3Yzv/IzZrkMMNxlmVMPqPmC X-Received: by 2002:a17:90a:be02:b0:263:829:2de with SMTP id a2-20020a17090abe0200b00263082902demr6160039pjs.2.1692005877652; Mon, 14 Aug 2023 02:37:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1692005877; cv=none; d=google.com; s=arc-20160816; b=QB9o32u19s7WD+Dgxa8ASVBMq/VVLAZ3Xh0VZEWrZHHPv755JpQ7LpPaTXQQqxdZE+ KaKHUY4/SOLBfN9BtgMOV5r3381oZZgrK7l6CwWf21hCs4EgAwujTvxAO25NTMheRvRY 3zh/ixYU4vEZNo7ak0lmbUHwUvq2R2sdqZtzD0dNyI0I5is73RqloJXPyyM/tD8cM8dS okWsfIDO44zdT+eSIsq/VdL3xvv0KbhXLVt79xeHLJjGLaL3C1f+2kV7E7x4A4Hwyn2E 4ZIr2ODEXKKv5h4QZEiG++mkTIY0OfSSc3Wc9QQLYtg8MUOirox3BblGxYUqlRu9mdkL vtsw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :dkim-signature:date; bh=8LcCKWr3ysRBu6jMqsm8nlz+BJEf48RXqJ1abrUpglc=; fh=TE4onW3Du2FQ1EsedDK8TzgRHOYndfJXYpOtlseWrM0=; b=RRQ5Md2Ji7/VjxDeI1nluGq5cgNGeLWqU/0u+bfbkJkLaqtZndw491DrqSLZ3Pp7by OZjYRikEgMRlE9hvSs7tQpahPW9HwlHMaWHNdmQrHSUt0yJWNOEG/vjLrIWX3wB8EjKF lEgBOxJApRv4uEghI45kYfyGx5XseCcYDz8EchAmsmUhMCzH6hQbP+sM4LSQvqM1fz7K YM1f43UCTfsTXRRsDZWHROua049xzruJFpWOOF+IXnctqXg1O+rzpXP0yRreXxqjbzLl AJkcZXCYTNPS8BNxBR1YC782dbKtyiMJBlMzx/6W8I6ukVPkLVWCvRlFdVCnSWZh5+uz 59Bg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=hyLGgOBI; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id b15-20020a170903228f00b001bc35017804si8014964plh.443.2023.08.14.02.37.45; Mon, 14 Aug 2023 02:37:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=hyLGgOBI; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234935AbjHNIqn (ORCPT + 99 others); Mon, 14 Aug 2023 04:46:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33916 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234370AbjHNIqf (ORCPT ); Mon, 14 Aug 2023 04:46:35 -0400 Received: from mail-40134.protonmail.ch (mail-40134.protonmail.ch [185.70.40.134]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B09F110B for ; Mon, 14 Aug 2023 01:46:32 -0700 (PDT) Date: Mon, 14 Aug 2023 08:46:26 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proton.me; s=protonmail; t=1692002790; x=1692261990; bh=8LcCKWr3ysRBu6jMqsm8nlz+BJEf48RXqJ1abrUpglc=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=hyLGgOBIBDVfL6VunfIeaigW2KFtEQYHVDQnd6yMyScHbItpxBtZF+pSpUzZt7nmw 7dcQwqIKbUKvgJaUzSYo846Q3Svv8EpEJvEB//qJtjVXoUTLR56pVl8zA0aSZIG31v YmVYUawdp0s/Zm2M6ifkBB/dqaZpfUXzt3qQOT4IJ+1CyIpGU0ih0jxvng7l02oBOl R2ItcbzojUjOhn/kFy0zzGtMwhl2ckstmeRa+CHCq28YTayA1XdzzMjQkdnELiQ0/B YmTRuOgXm9qcQon+QJtePEmDAx27hIoMYCsS7KC4NlAKXHWMQlNs/2eEP8bhyhfb1F tA4GgQpY+oYXA== To: Miguel Ojeda , Wedson Almeida Filho , Alex Gaynor From: Benno Lossin Cc: Boqun Feng , Gary Guo , =?utf-8?Q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Martin Rodriguez Reboredo Subject: [PATCH v4 01/13] rust: init: consolidate init macros Message-ID: <20230814084602.25699-2-benno.lossin@proton.me> In-Reply-To: <20230814084602.25699-1-benno.lossin@proton.me> References: <20230814084602.25699-1-benno.lossin@proton.me> Feedback-ID: 71780778:user:proton MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Merges the implementations of `try_init!` and `try_pin_init!`. These two macros are very similar, but use different traits. The new macro `__init_internal!` that is now the implementation for both takes these traits as parameters. This change does not affect any users, as no public API has been changed, but it should simplify maintaining the init macros. Reviewed-by: Bj=C3=B6rn Roy Baron Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Alice Ryhl Signed-off-by: Benno Lossin --- v3 -> v4: No changes. v2 -> v3: - added Reviewed-by's from Martin and Alice. v1 -> v2: - added Reviewed-by from Bj=C3=B6rn. rust/kernel/init.rs | 388 +++---------------------------------- rust/kernel/init/macros.rs | 237 +++++++++++++++++++++- 2 files changed, 259 insertions(+), 366 deletions(-) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index e05563aad2ed..d431d0b153a2 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -548,11 +548,14 @@ macro_rules! pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) =3D> { - $crate::try_pin_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error(::core::convert::Infallible), + @data(PinData, use_data), + @has_data(HasPinData, __pin_data), + @construct_closure(pin_init_from_closure), ) }; } @@ -601,205 +604,29 @@ macro_rules! try_pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) =3D> { - $crate::try_pin_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)? ), @fields($($fields)*), @error($crate::error::Error), + @data(PinData, use_data), + @has_data(HasPinData, __pin_data), + @construct_closure(pin_init_from_closure), ) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) =3D> { - $crate::try_pin_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)? ), @fields($($fields)*), @error($err), + @data(PinData, use_data), + @has_data(HasPinData, __pin_data), + @construct_closure(pin_init_from_closure), ) }; - ( - @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), - @fields($($fields:tt)*), - @error($err:ty), - ) =3D> {{ - // We do not want to allow arbitrary returns, so we declare this t= ype as the `Ok` return - // type and shadow it later when we insert the arbitrary user code= . That way there will be - // no possibility of returning without `unsafe`. - struct __InitOk; - // Get the pin data from the supplied type. - let data =3D unsafe { - use $crate::init::__internal::HasPinData; - $t$(::<$($generics),*>)?::__pin_data() - }; - // Ensure that `data` really is of type `PinData` and help with ty= pe inference: - let init =3D $crate::init::__internal::PinData::make_closure::<_, = __InitOk, $err>( - data, - move |slot| { - { - // Shadow the structure so it cannot be used to return= early. - struct __InitOk; - // Create the `this` so it can be referenced by the us= er inside of the - // expressions creating the individual fields. - $(let $this =3D unsafe { ::core::ptr::NonNull::new_unc= hecked(slot) };)? - // Initialize every field. - $crate::try_pin_init!(init_slot: - @data(data), - @slot(slot), - @munch_fields($($fields)*,), - ); - // We use unreachable code to ensure that all fields h= ave been mentioned exactly - // once, this struct initializer will still be type-ch= ecked and complain with a - // very natural error message if a field is forgotten/= mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expres= sion)] - if false { - $crate::try_pin_init!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - } - // Forget all guards, since initialization was a succe= ss. - $crate::try_pin_init!(forget_guards: - @munch_fields($($fields)*,), - ); - } - Ok(__InitOk) - } - ); - let init =3D move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - let init =3D unsafe { $crate::init::pin_init_from_closure::<_, $er= r>(init) }; - init - }}; - (init_slot: - @data($data:ident), - @slot($slot:ident), - @munch_fields($(,)?), - ) =3D> { - // Endpoint of munching, no fields are left. - }; - (init_slot: - @data($data:ident), - @slot($slot:ident), - // In-place initialization syntax. - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) =3D> { - let $field =3D $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initialize= r closure, we - // return when an error/panic occurs. - // We also use the `data` to require the correct trait (`Init` or = `PinInit`) for `$field`. - unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), = $field)? }; - // Create the drop guard. - // - // We only give access to `&DropGuard`, so it cannot be forgotten = via safe code. - // - // SAFETY: We forget the guard later when initialization has succe= eded. - let $field =3D &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) - }; - - $crate::try_pin_init!(init_slot: - @data($data), - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (init_slot: - @data($data:ident), - @slot($slot:ident), - // Direct value init, this is safe for every field. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) =3D> { - $(let $field =3D $val;)? - // Initialize the field. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$fi= eld), $field) }; - // Create the drop guard: - // - // We only give access to `&DropGuard`, so it cannot be accidental= ly forgotten. - // - // SAFETY: We forget the guard later when initialization has succe= eded. - let $field =3D &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) - }; - - $crate::try_pin_init!(init_slot: - @data($data), - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($(,)?), - @acc($($acc:tt)*), - ) =3D> { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the `if false` branch, this will never get exec= uted. We abuse `slot` to - // get the correct type inference here: - unsafe { - ::core::ptr::write($slot, $t { - $($acc)* - }); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - @acc($($acc:tt)*), - ) =3D> { - $crate::try_pin_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - @acc($($acc:tt)*), - ) =3D> { - $crate::try_pin_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; - (forget_guards: - @munch_fields($(,)?), - ) =3D> { - // Munching finished. - }; - (forget_guards: - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) =3D> { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_pin_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; - (forget_guards: - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) =3D> { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_pin_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; } =20 /// Construct an in-place initializer for `struct`s. @@ -824,11 +651,14 @@ macro_rules! init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) =3D> { - $crate::try_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error(::core::convert::Infallible), + @data(InitData, /*no use_data*/), + @has_data(HasInitData, __init_data), + @construct_closure(init_from_closure), ) } } @@ -871,199 +701,29 @@ macro_rules! try_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) =3D> { - $crate::try_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error($crate::error::Error), + @data(InitData, /*no use_data*/), + @has_data(HasInitData, __init_data), + @construct_closure(init_from_closure), ) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) =3D> { - $crate::try_init!( + $crate::__init_internal!( @this($($this)?), @typ($t $(::<$($generics),*>)?), @fields($($fields)*), @error($err), + @data(InitData, /*no use_data*/), + @has_data(HasInitData, __init_data), + @construct_closure(init_from_closure), ) }; - ( - @this($($this:ident)?), - @typ($t:ident $(::<$($generics:ty),*>)?), - @fields($($fields:tt)*), - @error($err:ty), - ) =3D> {{ - // We do not want to allow arbitrary returns, so we declare this t= ype as the `Ok` return - // type and shadow it later when we insert the arbitrary user code= . That way there will be - // no possibility of returning without `unsafe`. - struct __InitOk; - // Get the init data from the supplied type. - let data =3D unsafe { - use $crate::init::__internal::HasInitData; - $t$(::<$($generics),*>)?::__init_data() - }; - // Ensure that `data` really is of type `InitData` and help with t= ype inference: - let init =3D $crate::init::__internal::InitData::make_closure::<_,= __InitOk, $err>( - data, - move |slot| { - { - // Shadow the structure so it cannot be used to return= early. - struct __InitOk; - // Create the `this` so it can be referenced by the us= er inside of the - // expressions creating the individual fields. - $(let $this =3D unsafe { ::core::ptr::NonNull::new_unc= hecked(slot) };)? - // Initialize every field. - $crate::try_init!(init_slot: - @slot(slot), - @munch_fields($($fields)*,), - ); - // We use unreachable code to ensure that all fields h= ave been mentioned exactly - // once, this struct initializer will still be type-ch= ecked and complain with a - // very natural error message if a field is forgotten/= mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expres= sion)] - if false { - $crate::try_init!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - } - // Forget all guards, since initialization was a succe= ss. - $crate::try_init!(forget_guards: - @munch_fields($($fields)*,), - ); - } - Ok(__InitOk) - } - ); - let init =3D move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - let init =3D unsafe { $crate::init::init_from_closure::<_, $err>(i= nit) }; - init - }}; - (init_slot: - @slot($slot:ident), - @munch_fields( $(,)?), - ) =3D> { - // Endpoint of munching, no fields are left. - }; - (init_slot: - @slot($slot:ident), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) =3D> { - let $field =3D $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initialize= r closure, we - // return when an error/panic occurs. - unsafe { - $crate::init::Init::__init($field, ::core::ptr::addr_of_mut!((= *$slot).$field))?; - } - // Create the drop guard. - // - // We only give access to `&DropGuard`, so it cannot be accidental= ly forgotten. - // - // SAFETY: We forget the guard later when initialization has succe= eded. - let $field =3D &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) - }; - - $crate::try_init!(init_slot: - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (init_slot: - @slot($slot:ident), - // Direct value init. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) =3D> { - $(let $field =3D $val;)? - // Call the initializer. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$fi= eld), $field) }; - // Create the drop guard. - // - // We only give access to `&DropGuard`, so it cannot be accidental= ly forgotten. - // - // SAFETY: We forget the guard later when initialization has succe= eded. - let $field =3D &unsafe { - $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) - }; - - $crate::try_init!(init_slot: - @slot($slot), - @munch_fields($($rest)*), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields( $(,)?), - @acc($($acc:tt)*), - ) =3D> { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the `if false` branch, this will never get exec= uted. We abuse `slot` to - // get the correct type inference here: - unsafe { - ::core::ptr::write($slot, $t { - $($acc)* - }); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - @acc($($acc:tt)*), - ) =3D> { - $crate::try_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)*$field: ::core::panic!(),), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:ident), - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - @acc($($acc:tt)*), - ) =3D> { - $crate::try_init!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)*$field: ::core::panic!(),), - ); - }; - (forget_guards: - @munch_fields($(,)?), - ) =3D> { - // Munching finished. - }; - (forget_guards: - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) =3D> { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; - (forget_guards: - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) =3D> { - unsafe { $crate::init::__internal::DropGuard::forget($field) }; - - $crate::try_init!(forget_guards: - @munch_fields($($rest)*), - ); - }; } =20 /// A pin-initializer for the type `T`. diff --git a/rust/kernel/init/macros.rs b/rust/kernel/init/macros.rs index 00aa4e956c0a..fbaebd34f218 100644 --- a/rust/kernel/init/macros.rs +++ b/rust/kernel/init/macros.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT =20 //! This module provides the macros that actually implement the proc-macro= s `pin_data` and -//! `pinned_drop`. +//! `pinned_drop`. It also contains `__init_internal` the implementation o= f the `{try_}{pin_}init!` +//! macros. //! //! These macros should never be called directly, since they expect their = input to be -//! in a certain format which is internal. Use the proc-macros instead. +//! in a certain format which is internal. If used incorrectly, these macr= os can lead to UB even in +//! safe code! Use the public facing macros instead. //! //! This architecture has been chosen because the kernel does not yet have= access to `syn` which //! would make matters a lot easier for implementing these as proc-macros. @@ -980,3 +982,234 @@ impl<$($impl_generics)*> $pin_data<$($ty_generics)*> } }; } + +/// The internal init macro. Do not call manually! +/// +/// This is called by the `{try_}{pin_}init!` macros with various inputs. +/// +/// This macro has multiple internal call configurations, these are always= the very first ident: +/// - nothing: this is the base case and called by the `{try_}{pin_}init!`= macros. +/// - `init_slot`: recursively creates the code that initializes all field= s in `slot`. +/// - `make_initializer`: recursively create the struct initializer that g= uarantees that every +/// field has been initialized exactly once. +/// - `forget_guards`: recursively forget the drop guards for every field. +#[doc(hidden)] +#[macro_export] +macro_rules! __init_internal { + ( + @this($($this:ident)?), + @typ($t:ident $(::<$($generics:ty),*>)?), + @fields($($fields:tt)*), + @error($err:ty), + // Either `PinData` or `InitData`, `$use_data` should only be pres= ent in the `PinData` + // case. + @data($data:ident, $($use_data:ident)?), + // `HasPinData` or `HasInitData`. + @has_data($has_data:ident, $get_data:ident), + // `pin_init_from_closure` or `init_from_closure`. + @construct_closure($construct_closure:ident), + ) =3D> {{ + // We do not want to allow arbitrary returns, so we declare this t= ype as the `Ok` return + // type and shadow it later when we insert the arbitrary user code= . That way there will be + // no possibility of returning without `unsafe`. + struct __InitOk; + // Get the data about fields from the supplied type. + let data =3D unsafe { + use $crate::init::__internal::$has_data; + $t$(::<$($generics),*>)?::$get_data() + }; + // Ensure that `data` really is of type `$data` and help with type= inference: + let init =3D $crate::init::__internal::$data::make_closure::<_, __= InitOk, $err>( + data, + move |slot| { + { + // Shadow the structure so it cannot be used to return= early. + struct __InitOk; + // Create the `this` so it can be referenced by the us= er inside of the + // expressions creating the individual fields. + $(let $this =3D unsafe { ::core::ptr::NonNull::new_unc= hecked(slot) };)? + // Initialize every field. + $crate::__init_internal!(init_slot($($use_data)?): + @data(data), + @slot(slot), + @munch_fields($($fields)*,), + ); + // We use unreachable code to ensure that all fields h= ave been mentioned exactly + // once, this struct initializer will still be type-ch= ecked and complain with a + // very natural error message if a field is forgotten/= mentioned more than once. + #[allow(unreachable_code, clippy::diverging_sub_expres= sion)] + if false { + $crate::__init_internal!(make_initializer: + @slot(slot), + @type_name($t), + @munch_fields($($fields)*,), + @acc(), + ); + } + // Forget all guards, since initialization was a succe= ss. + $crate::__init_internal!(forget_guards: + @munch_fields($($fields)*,), + ); + } + Ok(__InitOk) + } + ); + let init =3D move |slot| -> ::core::result::Result<(), $err> { + init(slot).map(|__InitOk| ()) + }; + let init =3D unsafe { $crate::init::$construct_closure::<_, $err>(= init) }; + init + }}; + (init_slot($($use_data:ident)?): + @data($data:ident), + @slot($slot:ident), + @munch_fields($(,)?), + ) =3D> { + // Endpoint of munching, no fields are left. + }; + (init_slot($use_data:ident): // use_data is present, so we use the `da= ta` to init fields. + @data($data:ident), + @slot($slot:ident), + // In-place initialization syntax. + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + ) =3D> { + let $field =3D $val; + // Call the initializer. + // + // SAFETY: `slot` is valid, because we are inside of an initialize= r closure, we + // return when an error/panic occurs. + // We also use the `data` to require the correct trait (`Init` or = `PinInit`) for `$field`. + unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), = $field)? }; + // Create the drop guard. + // + // We only give access to `&DropGuard`, so it cannot be forgotten = via safe code. + // + // SAFETY: We forget the guard later when initialization has succe= eded. + let $field =3D &unsafe { + $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) + }; + + $crate::__init_internal!(init_slot($use_data): + @data($data), + @slot($slot), + @munch_fields($($rest)*), + ); + }; + (init_slot(): // no use_data, so we use `Init::__init` directly. + @data($data:ident), + @slot($slot:ident), + // In-place initialization syntax. + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + ) =3D> { + let $field =3D $val; + // Call the initializer. + // + // SAFETY: `slot` is valid, because we are inside of an initialize= r closure, we + // return when an error/panic occurs. + unsafe { $crate::init::Init::__init($field, ::core::ptr::addr_of_m= ut!((*$slot).$field))? }; + // Create the drop guard. + // + // We only give access to `&DropGuard`, so it cannot be forgotten = via safe code. + // + // SAFETY: We forget the guard later when initialization has succe= eded. + let $field =3D &unsafe { + $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) + }; + + $crate::__init_internal!(init_slot(): + @data($data), + @slot($slot), + @munch_fields($($rest)*), + ); + }; + (init_slot($($use_data:ident)?): + @data($data:ident), + @slot($slot:ident), + // Init by-value. + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + ) =3D> { + $(let $field =3D $val;)? + // Initialize the field. + // + // SAFETY: The memory at `slot` is uninitialized. + unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$fi= eld), $field) }; + // Create the drop guard: + // + // We only give access to `&DropGuard`, so it cannot be accidental= ly forgotten. + // + // SAFETY: We forget the guard later when initialization has succe= eded. + let $field =3D &unsafe { + $crate::init::__internal::DropGuard::new(::core::ptr::addr_of_= mut!((*$slot).$field)) + }; + + $crate::__init_internal!(init_slot($($use_data)?): + @data($data), + @slot($slot), + @munch_fields($($rest)*), + ); + }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields($(,)?), + @acc($($acc:tt)*), + ) =3D> { + // Endpoint, nothing more to munch, create the initializer. + // Since we are in the `if false` branch, this will never get exec= uted. We abuse `slot` to + // get the correct type inference here: + unsafe { + ::core::ptr::write($slot, $t { + $($acc)* + }); + } + }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + @acc($($acc:tt)*), + ) =3D> { + $crate::__init_internal!(make_initializer: + @slot($slot), + @type_name($t), + @munch_fields($($rest)*), + @acc($($acc)* $field: ::core::panic!(),), + ); + }; + (make_initializer: + @slot($slot:ident), + @type_name($t:ident), + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + @acc($($acc:tt)*), + ) =3D> { + $crate::__init_internal!(make_initializer: + @slot($slot), + @type_name($t), + @munch_fields($($rest)*), + @acc($($acc)* $field: ::core::panic!(),), + ); + }; + (forget_guards: + @munch_fields($(,)?), + ) =3D> { + // Munching finished. + }; + (forget_guards: + @munch_fields($field:ident <- $val:expr, $($rest:tt)*), + ) =3D> { + unsafe { $crate::init::__internal::DropGuard::forget($field) }; + + $crate::__init_internal!(forget_guards: + @munch_fields($($rest)*), + ); + }; + (forget_guards: + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + ) =3D> { + unsafe { $crate::init::__internal::DropGuard::forget($field) }; + + $crate::__init_internal!(forget_guards: + @munch_fields($($rest)*), + ); + }; +} --=20 2.41.0