Skip to content
Open
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
22 changes: 7 additions & 15 deletions internal/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,6 @@ pub(crate) fn expand(
);
let field_check = make_field_check(&fields, init_kind, &path);
Ok(quote! {{
// We do not want to allow arbitrary returns, so we declare this type 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.
// SAFETY: TODO
let #data = unsafe {
Expand All @@ -170,18 +165,15 @@ pub(crate) fn expand(
#path::#get_data()
};
// Ensure that `#data` really is of type `#data` and help with type inference:
let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>(
let init = ::pin_init::__internal::#data_trait::make_closure::<_, #error>(
#data,
move |slot| {
{
// Shadow the structure so it cannot be used to return early.
struct __InitOk;
#zeroable_check
#this
#init_fields
#field_check
}
Ok(__InitOk)
#zeroable_check
#this
#init_fields
#field_check
// SAFETY: we are the `init!` macro that is allowed to call this.
Ok(unsafe { ::pin_init::__internal::InitOk::new() })
}
);
let init = move |slot| -> ::core::result::Result<(), #error> {
Expand Down
28 changes: 24 additions & 4 deletions src/__internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ where
}
}

/// Token type to signify successful initialization.
///
/// Can only be constructed via the unsafe [`Self::new`] function. The initializer macros use this
/// token type to prevent returning `Ok` from an initializer without initializing all fields.
pub struct InitOk(());

impl InitOk {
/// Creates a new token.
///
/// # Safety
///
/// This function may only be called from the `init!` macro in `../internal/src/init.rs`.
#[inline(always)]
pub unsafe fn new() -> Self {
Self(())
}
}

/// This trait is only implemented via the `#[pin_data]` proc-macro. It is used to facilitate
/// the pin projections within the initializers.
///
Expand All @@ -68,9 +86,10 @@ pub unsafe trait PinData: Copy {
type Datee: ?Sized + HasPinData;

/// Type inference helper function.
fn make_closure<F, O, E>(self, f: F) -> F
#[inline(always)]
fn make_closure<F, E>(self, f: F) -> F
where
F: FnOnce(*mut Self::Datee) -> Result<O, E>,
F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
{
f
}
Expand Down Expand Up @@ -98,9 +117,10 @@ pub unsafe trait InitData: Copy {
type Datee: ?Sized + HasInitData;

/// Type inference helper function.
fn make_closure<F, O, E>(self, f: F) -> F
#[inline(always)]
fn make_closure<F, E>(self, f: F) -> F
where
F: FnOnce(*mut Self::Datee) -> Result<O, E>,
F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
{
f
}
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/compile-fail/init/early_return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use pin_init::*;

struct Foo {
a: usize,
}

fn main() {
let _ = init!(Foo {
_: {
return Ok(());
},
a: 42,
});
}
36 changes: 36 additions & 0 deletions tests/ui/compile-fail/init/early_return.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
error[E0308]: mismatched types
--> tests/ui/compile-fail/init/early_return.rs:10:23
|
10 | return Ok(());
| -- ^^ expected `InitOk`, found `()`
| |
| arguments to this enum variant are incorrect
|
help: the type constructed contains `()` due to the type of the argument passed
--> tests/ui/compile-fail/init/early_return.rs:10:20
|
10 | return Ok(());
| ^^^--^
| |
| this argument influences the type of `Ok`
note: tuple variant defined here
--> $RUST/core/src/result.rs
|
| Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
| ^^

warning: unreachable statement
--> tests/ui/compile-fail/init/early_return.rs:8:13
|
8 | let _ = init!(Foo {
| _____________^
9 | | _: {
10 | | return Ok(());
| | ------------- any code following this expression is unreachable
11 | | },
12 | | a: 42,
13 | | });
| |______^ 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)
73 changes: 34 additions & 39 deletions tests/ui/expand/no_field_access.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,52 @@ struct Foo {
}
fn main() {
let _ = {
struct __InitOk;
let __data = unsafe {
use ::pin_init::__internal::HasInitData;
Foo::__init_data()
};
let init = ::pin_init::__internal::InitData::make_closure::<
_,
__InitOk,
::core::convert::Infallible,
>(
__data,
move |slot| {
{
struct __InitOk;
{
let c = -42;
unsafe { ::core::ptr::write(&raw mut (*slot).c, c) };
}
let __c_guard = unsafe {
::pin_init::__internal::DropGuard::new(&raw mut (*slot).c)
};
{
let b = *c;
unsafe { ::core::ptr::write(&raw mut (*slot).b, b) };
}
let __b_guard = unsafe {
::pin_init::__internal::DropGuard::new(&raw mut (*slot).b)
};
{
let a = 0;
unsafe { ::core::ptr::write(&raw mut (*slot).a, a) };
}
let __a_guard = unsafe {
::pin_init::__internal::DropGuard::new(&raw mut (*slot).a)
};
::core::mem::forget(__c_guard);
::core::mem::forget(__b_guard);
::core::mem::forget(__a_guard);
#[allow(unreachable_code, clippy::diverging_sub_expression)]
let _ = || unsafe {
::core::ptr::write(
slot,
Foo {
c: ::core::panicking::panic("explicit panic"),
b: ::core::panicking::panic("explicit panic"),
a: ::core::panicking::panic("explicit panic"),
},
)
};
let c = -42;
unsafe { ::core::ptr::write(&raw mut (*slot).c, c) };
}
Ok(__InitOk)
let __c_guard = unsafe {
::pin_init::__internal::DropGuard::new(&raw mut (*slot).c)
};
{
let b = *c;
unsafe { ::core::ptr::write(&raw mut (*slot).b, b) };
}
let __b_guard = unsafe {
::pin_init::__internal::DropGuard::new(&raw mut (*slot).b)
};
{
let a = 0;
unsafe { ::core::ptr::write(&raw mut (*slot).a, a) };
}
let __a_guard = unsafe {
::pin_init::__internal::DropGuard::new(&raw mut (*slot).a)
};
::core::mem::forget(__c_guard);
::core::mem::forget(__b_guard);
::core::mem::forget(__a_guard);
#[allow(unreachable_code, clippy::diverging_sub_expression)]
let _ = || unsafe {
::core::ptr::write(
slot,
Foo {
c: ::core::panicking::panic("explicit panic"),
b: ::core::panicking::panic("explicit panic"),
a: ::core::panicking::panic("explicit panic"),
},
)
};
Ok(unsafe { ::pin_init::__internal::InitOk::new() })
},
);
let init = move |
Expand Down
11 changes: 3 additions & 8 deletions tests/ui/expand/simple-init.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,19 @@ use pin_init::*;
struct Foo {}
fn main() {
let _ = {
struct __InitOk;
let __data = unsafe {
use ::pin_init::__internal::HasInitData;
Foo::__init_data()
};
let init = ::pin_init::__internal::InitData::make_closure::<
_,
__InitOk,
::core::convert::Infallible,
>(
__data,
move |slot| {
{
struct __InitOk;
#[allow(unreachable_code, clippy::diverging_sub_expression)]
let _ = || unsafe { ::core::ptr::write(slot, Foo {}) };
}
Ok(__InitOk)
#[allow(unreachable_code, clippy::diverging_sub_expression)]
let _ = || unsafe { ::core::ptr::write(slot, Foo {}) };
Ok(unsafe { ::pin_init::__internal::InitOk::new() })
},
);
let init = move |
Expand Down