Merge a031fe8d1d ("Merge tag 'rust-6.6' of https://github.com/Rust-for-Linux/linux") into android-mainline
Steps on the way to 6.6-rc1 Resolves merge conflicts in: scripts/Makefile.modfinal Change-Id: Ied16f15024fb40b0d679b9b70a7ca42b13ead1e5 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
@@ -31,8 +31,8 @@ you probably needn't concern yourself with pcmciautils.
|
||||
====================== =============== ========================================
|
||||
GNU C 5.1 gcc --version
|
||||
Clang/LLVM (optional) 11.0.0 clang --version
|
||||
Rust (optional) 1.68.2 rustc --version
|
||||
bindgen (optional) 0.56.0 bindgen --version
|
||||
Rust (optional) 1.71.1 rustc --version
|
||||
bindgen (optional) 0.65.1 bindgen --version
|
||||
GNU make 3.82 make --version
|
||||
bash 4.2 bash --version
|
||||
binutils 2.25 ld -v
|
||||
|
||||
@@ -38,7 +38,9 @@ and run::
|
||||
|
||||
rustup override set $(scripts/min-tool-version.sh rustc)
|
||||
|
||||
Otherwise, fetch a standalone installer from:
|
||||
This will configure your working directory to use the correct version of
|
||||
``rustc`` without affecting your default toolchain. If you are not using
|
||||
``rustup``, fetch a standalone installer from:
|
||||
|
||||
https://forge.rust-lang.org/infra/other-installation-methods.html#standalone
|
||||
|
||||
@@ -56,16 +58,17 @@ If ``rustup`` is being used, run::
|
||||
The components are installed per toolchain, thus upgrading the Rust compiler
|
||||
version later on requires re-adding the component.
|
||||
|
||||
Otherwise, if a standalone installer is used, the Rust repository may be cloned
|
||||
into the installation folder of the toolchain::
|
||||
Otherwise, if a standalone installer is used, the Rust source tree may be
|
||||
downloaded into the toolchain's installation folder::
|
||||
|
||||
git clone --recurse-submodules \
|
||||
--branch $(scripts/min-tool-version.sh rustc) \
|
||||
https://github.com/rust-lang/rust \
|
||||
$(rustc --print sysroot)/lib/rustlib/src/rust
|
||||
curl -L "https://static.rust-lang.org/dist/rust-src-$(scripts/min-tool-version.sh rustc).tar.gz" |
|
||||
tar -xzf - -C "$(rustc --print sysroot)/lib" \
|
||||
"rust-src-$(scripts/min-tool-version.sh rustc)/rust-src/lib/" \
|
||||
--strip-components=3
|
||||
|
||||
In this case, upgrading the Rust compiler version later on requires manually
|
||||
updating this clone.
|
||||
updating the source tree (this can be done by removing ``$(rustc --print
|
||||
sysroot)/lib/rustlib/src/rust`` then rerunning the above command).
|
||||
|
||||
|
||||
libclang
|
||||
@@ -98,7 +101,24 @@ the ``bindgen`` tool. A particular version is required.
|
||||
|
||||
Install it via (note that this will download and build the tool from source)::
|
||||
|
||||
cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen
|
||||
cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
|
||||
|
||||
``bindgen`` needs to find a suitable ``libclang`` in order to work. If it is
|
||||
not found (or a different ``libclang`` than the one found should be used),
|
||||
the process can be tweaked using the environment variables understood by
|
||||
``clang-sys`` (the Rust bindings crate that ``bindgen`` uses to access
|
||||
``libclang``):
|
||||
|
||||
* ``LLVM_CONFIG_PATH`` can be pointed to an ``llvm-config`` executable.
|
||||
|
||||
* Or ``LIBCLANG_PATH`` can be pointed to a ``libclang`` shared library
|
||||
or to the directory containing it.
|
||||
|
||||
* Or ``CLANG_PATH`` can be pointed to a ``clang`` executable.
|
||||
|
||||
For details, please see ``clang-sys``'s documentation at:
|
||||
|
||||
https://github.com/KyleMayes/clang-sys#environment-variables
|
||||
|
||||
|
||||
Requirements: Developing
|
||||
@@ -179,7 +199,9 @@ be used with many editors to enable syntax highlighting, completion, go to
|
||||
definition, and other features.
|
||||
|
||||
``rust-analyzer`` needs a configuration file, ``rust-project.json``, which
|
||||
can be generated by the ``rust-analyzer`` Make target.
|
||||
can be generated by the ``rust-analyzer`` Make target::
|
||||
|
||||
make LLVM=1 rust-analyzer
|
||||
|
||||
|
||||
Configuration
|
||||
|
||||
@@ -18584,6 +18584,8 @@ R: Boqun Feng <boqun.feng@gmail.com>
|
||||
R: Gary Guo <gary@garyguo.net>
|
||||
R: Björn Roy Baron <bjorn3_gh@protonmail.com>
|
||||
R: Benno Lossin <benno.lossin@proton.me>
|
||||
R: Andreas Hindborg <a.hindborg@samsung.com>
|
||||
R: Alice Ryhl <aliceryhl@google.com>
|
||||
L: rust-for-linux@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://github.com/Rust-for-Linux/linux
|
||||
|
||||
@@ -485,6 +485,7 @@ export rust_common_flags := --edition=2021 \
|
||||
-Dclippy::let_unit_value -Dclippy::mut_mut \
|
||||
-Dclippy::needless_bitwise_bool \
|
||||
-Dclippy::needless_continue \
|
||||
-Dclippy::no_mangle_with_rust_abi \
|
||||
-Wclippy::dbg_macro
|
||||
|
||||
KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS)
|
||||
@@ -1351,7 +1352,7 @@ prepare0: archprepare
|
||||
# All the preparing..
|
||||
prepare: prepare0
|
||||
ifdef CONFIG_RUST
|
||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh -v
|
||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh
|
||||
$(Q)$(MAKE) $(build)=rust
|
||||
endif
|
||||
|
||||
@@ -1863,7 +1864,7 @@ $(DOC_TARGETS):
|
||||
# "Is Rust available?" target
|
||||
PHONY += rustavailable
|
||||
rustavailable:
|
||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh -v && echo "Rust is available!"
|
||||
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh && echo "Rust is available!"
|
||||
|
||||
# Documentation target
|
||||
#
|
||||
@@ -1897,11 +1898,6 @@ rustfmt:
|
||||
rustfmtcheck: rustfmt_flags = --check
|
||||
rustfmtcheck: rustfmt
|
||||
|
||||
# IDE support targets
|
||||
PHONY += rust-analyzer
|
||||
rust-analyzer:
|
||||
$(Q)$(MAKE) $(build)=rust $@
|
||||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1964,6 +1960,7 @@ help:
|
||||
@echo ' headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH'
|
||||
@echo ' (default: $(abspath $(INSTALL_HDR_PATH)))'
|
||||
@echo ' clean - remove generated files in module directory only'
|
||||
@echo ' rust-analyzer - generate rust-project.json rust-analyzer support file'
|
||||
@echo ''
|
||||
|
||||
__external_modules_error:
|
||||
@@ -2107,6 +2104,11 @@ quiet_cmd_tags = GEN $@
|
||||
tags TAGS cscope gtags: FORCE
|
||||
$(call cmd,tags)
|
||||
|
||||
# IDE support targets
|
||||
PHONY += rust-analyzer
|
||||
rust-analyzer:
|
||||
$(Q)$(MAKE) $(build)=rust $@
|
||||
|
||||
# Script to generate missing namespace dependencies
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
+10
-7
@@ -329,7 +329,7 @@ quiet_cmd_bindgen = BINDGEN $@
|
||||
$(BINDGEN) $< $(bindgen_target_flags) \
|
||||
--use-core --with-derive-default --ctypes-prefix core::ffi --no-layout-tests \
|
||||
--no-debug '.*' \
|
||||
--size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE \
|
||||
-o $@ -- $(bindgen_c_flags_final) -DMODULE \
|
||||
$(bindgen_target_cflags) $(bindgen_target_extra)
|
||||
|
||||
$(obj)/bindings/bindings_generated.rs: private bindgen_target_flags = \
|
||||
@@ -349,8 +349,8 @@ $(obj)/uapi/uapi_generated.rs: $(src)/uapi/uapi_helper.h \
|
||||
# given it is `libclang`; but for consistency, future Clang changes and/or
|
||||
# a potential future GCC backend for `bindgen`, we disable it too.
|
||||
$(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_flags = \
|
||||
--blacklist-type '.*' --whitelist-var '' \
|
||||
--whitelist-function 'rust_helper_.*'
|
||||
--blocklist-type '.*' --allowlist-var '' \
|
||||
--allowlist-function 'rust_helper_.*'
|
||||
$(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_cflags = \
|
||||
-I$(objtree)/$(obj) -Wno-missing-prototypes -Wno-missing-declarations
|
||||
$(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; \
|
||||
@@ -402,12 +402,15 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
|
||||
$(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@)
|
||||
|
||||
rust-analyzer:
|
||||
$(Q)$(srctree)/scripts/generate_rust_analyzer.py $(srctree) $(objtree) \
|
||||
$(RUST_LIB_SRC) > $(objtree)/rust-project.json
|
||||
$(Q)$(srctree)/scripts/generate_rust_analyzer.py \
|
||||
--cfgs='core=$(core-cfgs)' --cfgs='alloc=$(alloc-cfgs)' \
|
||||
$(abs_srctree) $(abs_objtree) \
|
||||
$(RUST_LIB_SRC) $(KBUILD_EXTMOD) > \
|
||||
$(if $(KBUILD_EXTMOD),$(extmod_prefix),$(objtree))/rust-project.json
|
||||
|
||||
redirect-intrinsics = \
|
||||
__eqsf2 __gesf2 __lesf2 __nesf2 __unordsf2 \
|
||||
__unorddf2 \
|
||||
__addsf3 __eqsf2 __gesf2 __lesf2 __ltsf2 __mulsf3 __nesf2 __unordsf2 \
|
||||
__adddf3 __ledf2 __ltdf2 __muldf3 __unorddf2 \
|
||||
__muloti4 __multi3 \
|
||||
__udivmodti4 __udivti3 __umodti3
|
||||
|
||||
|
||||
+12
-8
@@ -16,8 +16,6 @@ use core::ptr::{self, NonNull};
|
||||
#[doc(inline)]
|
||||
pub use core::alloc::*;
|
||||
|
||||
use core::marker::Destruct;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -41,6 +39,9 @@ extern "Rust" {
|
||||
#[rustc_allocator_zeroed]
|
||||
#[rustc_nounwind]
|
||||
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
static __rust_no_alloc_shim_is_unstable: u8;
|
||||
}
|
||||
|
||||
/// The global memory allocator.
|
||||
@@ -94,7 +95,14 @@ pub use std::alloc::Global;
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[inline]
|
||||
pub unsafe fn alloc(layout: Layout) -> *mut u8 {
|
||||
unsafe { __rust_alloc(layout.size(), layout.align()) }
|
||||
unsafe {
|
||||
// Make sure we don't accidentally allow omitting the allocator shim in
|
||||
// stable code until it is actually stabilized.
|
||||
#[cfg(not(bootstrap))]
|
||||
core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable);
|
||||
|
||||
__rust_alloc(layout.size(), layout.align())
|
||||
}
|
||||
}
|
||||
|
||||
/// Deallocate memory with the global allocator.
|
||||
@@ -333,16 +341,12 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
|
||||
|
||||
#[cfg_attr(not(test), lang = "box_free")]
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
// This signature has to be the same as `Box`, otherwise an ICE will happen.
|
||||
// When an additional parameter to `Box` is added (like `A: Allocator`), this has to be added here as
|
||||
// well.
|
||||
// For example if `Box` is changed to `struct Box<T: ?Sized, A: Allocator>(Unique<T>, A)`,
|
||||
// this function has to be changed to `fn box_free<T: ?Sized, A: Allocator>(Unique<T>, A)` as well.
|
||||
pub(crate) const unsafe fn box_free<T: ?Sized, A: ~const Allocator + ~const Destruct>(
|
||||
ptr: Unique<T>,
|
||||
alloc: A,
|
||||
) {
|
||||
pub(crate) unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A) {
|
||||
unsafe {
|
||||
let size = size_of_val(ptr.as_ref());
|
||||
let align = min_align_of_val(ptr.as_ref());
|
||||
|
||||
+64
-67
@@ -152,16 +152,13 @@ use core::any::Any;
|
||||
use core::async_iter::AsyncIterator;
|
||||
use core::borrow;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert::{From, TryFrom};
|
||||
use core::error::Error;
|
||||
use core::fmt;
|
||||
use core::future::Future;
|
||||
use core::hash::{Hash, Hasher};
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::iter::FromIterator;
|
||||
use core::iter::{FusedIterator, Iterator};
|
||||
use core::iter::FusedIterator;
|
||||
use core::marker::Tuple;
|
||||
use core::marker::{Destruct, Unpin, Unsize};
|
||||
use core::marker::Unsize;
|
||||
use core::mem;
|
||||
use core::ops::{
|
||||
CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Generator, GeneratorState, Receiver,
|
||||
@@ -218,6 +215,7 @@ impl<T> Box<T> {
|
||||
#[inline(always)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
#[rustc_diagnostic_item = "box_new"]
|
||||
pub fn new(x: T) -> Self {
|
||||
#[rustc_box]
|
||||
Box::new(x)
|
||||
@@ -287,9 +285,7 @@ impl<T> Box<T> {
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn pin(x: T) -> Pin<Box<T>> {
|
||||
(#[rustc_box]
|
||||
Box::new(x))
|
||||
.into()
|
||||
Box::new(x).into()
|
||||
}
|
||||
|
||||
/// Allocates memory on the heap then places `x` into it,
|
||||
@@ -381,12 +377,11 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// ```
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new_in(x: T, alloc: A) -> Self
|
||||
pub fn new_in(x: T, alloc: A) -> Self
|
||||
where
|
||||
A: ~const Allocator + ~const Destruct,
|
||||
A: Allocator,
|
||||
{
|
||||
let mut boxed = Self::new_uninit_in(alloc);
|
||||
unsafe {
|
||||
@@ -411,12 +406,10 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
pub const fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError>
|
||||
pub fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError>
|
||||
where
|
||||
T: ~const Destruct,
|
||||
A: ~const Allocator + ~const Destruct,
|
||||
A: Allocator,
|
||||
{
|
||||
let mut boxed = Self::try_new_uninit_in(alloc)?;
|
||||
unsafe {
|
||||
@@ -446,13 +439,12 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// assert_eq!(*five, 5)
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[must_use]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub const fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
|
||||
pub fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
|
||||
where
|
||||
A: ~const Allocator + ~const Destruct,
|
||||
A: Allocator,
|
||||
{
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
// NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable.
|
||||
@@ -487,10 +479,9 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
pub const fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
|
||||
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
|
||||
where
|
||||
A: ~const Allocator + ~const Destruct,
|
||||
A: Allocator,
|
||||
{
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
let ptr = alloc.allocate(layout)?.cast();
|
||||
@@ -518,13 +509,12 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
///
|
||||
/// [zeroed]: mem::MaybeUninit::zeroed
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[must_use]
|
||||
pub const fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
|
||||
pub fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A>
|
||||
where
|
||||
A: ~const Allocator + ~const Destruct,
|
||||
A: Allocator,
|
||||
{
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
// NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable.
|
||||
@@ -559,10 +549,9 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// [zeroed]: mem::MaybeUninit::zeroed
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
pub const fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
|
||||
pub fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError>
|
||||
where
|
||||
A: ~const Allocator + ~const Destruct,
|
||||
A: Allocator,
|
||||
{
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
let ptr = alloc.allocate_zeroed(layout)?.cast();
|
||||
@@ -578,12 +567,11 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// construct a (pinned) `Box` in a different way than with [`Box::new_in`].
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub const fn pin_in(x: T, alloc: A) -> Pin<Self>
|
||||
pub fn pin_in(x: T, alloc: A) -> Pin<Self>
|
||||
where
|
||||
A: 'static + ~const Allocator + ~const Destruct,
|
||||
A: 'static + Allocator,
|
||||
{
|
||||
Self::into_pin(Self::new_in(x, alloc))
|
||||
}
|
||||
@@ -592,8 +580,7 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
///
|
||||
/// This conversion does not allocate on the heap and happens in place.
|
||||
#[unstable(feature = "box_into_boxed_slice", issue = "71582")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
pub const fn into_boxed_slice(boxed: Self) -> Box<[T], A> {
|
||||
pub fn into_boxed_slice(boxed: Self) -> Box<[T], A> {
|
||||
let (raw, alloc) = Box::into_raw_with_allocator(boxed);
|
||||
unsafe { Box::from_raw_in(raw as *mut [T; 1], alloc) }
|
||||
}
|
||||
@@ -610,12 +597,8 @@ impl<T, A: Allocator> Box<T, A> {
|
||||
/// assert_eq!(Box::into_inner(c), 5);
|
||||
/// ```
|
||||
#[unstable(feature = "box_into_inner", issue = "80437")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
pub const fn into_inner(boxed: Self) -> T
|
||||
where
|
||||
Self: ~const Destruct,
|
||||
{
|
||||
pub fn into_inner(boxed: Self) -> T {
|
||||
*boxed
|
||||
}
|
||||
}
|
||||
@@ -829,9 +812,8 @@ impl<T, A: Allocator> Box<mem::MaybeUninit<T>, A> {
|
||||
/// assert_eq!(*five, 5)
|
||||
/// ```
|
||||
#[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
pub const unsafe fn assume_init(self) -> Box<T, A> {
|
||||
pub unsafe fn assume_init(self) -> Box<T, A> {
|
||||
let (raw, alloc) = Box::into_raw_with_allocator(self);
|
||||
unsafe { Box::from_raw_in(raw as *mut T, alloc) }
|
||||
}
|
||||
@@ -864,9 +846,8 @@ impl<T, A: Allocator> Box<mem::MaybeUninit<T>, A> {
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
pub const fn write(mut boxed: Self, value: T) -> Box<T, A> {
|
||||
pub fn write(mut boxed: Self, value: T) -> Box<T, A> {
|
||||
unsafe {
|
||||
(*boxed).write(value);
|
||||
boxed.assume_init()
|
||||
@@ -1110,9 +1091,8 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
||||
///
|
||||
/// [memory layout]: self#memory-layout
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
pub const fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
|
||||
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
|
||||
let (leaked, alloc) = Box::into_unique(b);
|
||||
(leaked.as_ptr(), alloc)
|
||||
}
|
||||
@@ -1122,10 +1102,9 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
||||
issue = "none",
|
||||
reason = "use `Box::leak(b).into()` or `Unique::from(Box::leak(b))` instead"
|
||||
)]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub const fn into_unique(b: Self) -> (Unique<T>, A) {
|
||||
pub fn into_unique(b: Self) -> (Unique<T>, A) {
|
||||
// Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a
|
||||
// raw pointer for the type system. Turning it directly into a raw pointer would not be
|
||||
// recognized as "releasing" the unique pointer to permit aliased raw accesses,
|
||||
@@ -1183,9 +1162,8 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
||||
/// assert_eq!(*static_ref, [4, 2, 3]);
|
||||
/// ```
|
||||
#[stable(feature = "box_leak", since = "1.26.0")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
#[inline]
|
||||
pub const fn leak<'a>(b: Self) -> &'a mut T
|
||||
pub fn leak<'a>(b: Self) -> &'a mut T
|
||||
where
|
||||
A: 'a,
|
||||
{
|
||||
@@ -1246,16 +1224,16 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: Default> Default for Box<T> {
|
||||
/// Creates a `Box<T>`, with the `Default` value for T.
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
#[rustc_box]
|
||||
Box::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
|
||||
impl<T> const Default for Box<[T]> {
|
||||
impl<T> Default for Box<[T]> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
let ptr: Unique<[T]> = Unique::<[T; 0]>::dangling();
|
||||
Box(ptr, Global)
|
||||
@@ -1264,8 +1242,8 @@ impl<T> const Default for Box<[T]> {
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "default_box_extra", since = "1.17.0")]
|
||||
#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
|
||||
impl const Default for Box<str> {
|
||||
impl Default for Box<str> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
// SAFETY: This is the same as `Unique::cast<U>` but with an unsized `U = str`.
|
||||
let ptr: Unique<str> = unsafe {
|
||||
@@ -1461,8 +1439,7 @@ impl<T> From<T> for Box<T> {
|
||||
}
|
||||
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
impl<T: ?Sized, A: Allocator> const From<Box<T, A>> for Pin<Box<T, A>>
|
||||
impl<T: ?Sized, A: Allocator> From<Box<T, A>> for Pin<Box<T, A>>
|
||||
where
|
||||
A: 'static,
|
||||
{
|
||||
@@ -1482,9 +1459,36 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization trait used for `From<&[T]>`.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
trait BoxFromSlice<T> {
|
||||
fn from_slice(slice: &[T]) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl<T: Clone> BoxFromSlice<T> for Box<[T]> {
|
||||
#[inline]
|
||||
default fn from_slice(slice: &[T]) -> Self {
|
||||
slice.to_vec().into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl<T: Copy> BoxFromSlice<T> for Box<[T]> {
|
||||
#[inline]
|
||||
fn from_slice(slice: &[T]) -> Self {
|
||||
let len = slice.len();
|
||||
let buf = RawVec::with_capacity(len);
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len);
|
||||
buf.into_box(slice.len()).assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "box_from_slice", since = "1.17.0")]
|
||||
impl<T: Copy> From<&[T]> for Box<[T]> {
|
||||
impl<T: Clone> From<&[T]> for Box<[T]> {
|
||||
/// Converts a `&[T]` into a `Box<[T]>`
|
||||
///
|
||||
/// This conversion allocates on the heap
|
||||
@@ -1498,19 +1502,15 @@ impl<T: Copy> From<&[T]> for Box<[T]> {
|
||||
///
|
||||
/// println!("{boxed_slice:?}");
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from(slice: &[T]) -> Box<[T]> {
|
||||
let len = slice.len();
|
||||
let buf = RawVec::with_capacity(len);
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len);
|
||||
buf.into_box(slice.len()).assume_init()
|
||||
}
|
||||
<Self as BoxFromSlice<T>>::from_slice(slice)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "box_from_cow", since = "1.45.0")]
|
||||
impl<T: Copy> From<Cow<'_, [T]>> for Box<[T]> {
|
||||
impl<T: Clone> From<Cow<'_, [T]>> for Box<[T]> {
|
||||
/// Converts a `Cow<'_, [T]>` into a `Box<[T]>`
|
||||
///
|
||||
/// When `cow` is the `Cow::Borrowed` variant, this
|
||||
@@ -1620,7 +1620,6 @@ impl<T, const N: usize> From<[T; N]> for Box<[T]> {
|
||||
/// println!("{boxed:?}");
|
||||
/// ```
|
||||
fn from(array: [T; N]) -> Box<[T]> {
|
||||
#[rustc_box]
|
||||
Box::new(array)
|
||||
}
|
||||
}
|
||||
@@ -1899,8 +1898,7 @@ impl<T: ?Sized, A: Allocator> fmt::Pointer for Box<T, A> {
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
impl<T: ?Sized, A: Allocator> const Deref for Box<T, A> {
|
||||
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
@@ -1909,8 +1907,7 @@ impl<T: ?Sized, A: Allocator> const Deref for Box<T, A> {
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
|
||||
impl<T: ?Sized, A: Allocator> const DerefMut for Box<T, A> {
|
||||
impl<T: ?Sized, A: Allocator> DerefMut for Box<T, A> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut **self
|
||||
}
|
||||
|
||||
+26
-24
@@ -89,35 +89,37 @@
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![allow(explicit_outlives_requirements)]
|
||||
#![warn(multiple_supertrait_upcastable)]
|
||||
//
|
||||
// Library features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))]
|
||||
#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))]
|
||||
#![cfg_attr(test, feature(is_sorted))]
|
||||
#![cfg_attr(test, feature(new_uninit))]
|
||||
#![feature(alloc_layout_extra)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(array_chunks)]
|
||||
#![feature(array_into_iter_constructors)]
|
||||
#![feature(array_methods)]
|
||||
#![feature(array_windows)]
|
||||
#![feature(ascii_char)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(async_iterator)]
|
||||
#![feature(coerce_unsized)]
|
||||
#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))]
|
||||
#![feature(const_box)]
|
||||
#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))]
|
||||
#![cfg_attr(not(no_borrow), feature(const_cow_is_borrowed))]
|
||||
#![feature(const_convert)]
|
||||
#![feature(const_size_of_val)]
|
||||
#![feature(const_align_of_val)]
|
||||
#![feature(const_ptr_read)]
|
||||
#![feature(const_maybe_uninit_zeroed)]
|
||||
#![feature(const_maybe_uninit_write)]
|
||||
#![feature(const_box)]
|
||||
#![cfg_attr(not(no_borrow), feature(const_cow_is_borrowed))]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_maybe_uninit_write)]
|
||||
#![feature(const_maybe_uninit_zeroed)]
|
||||
#![feature(const_pin)]
|
||||
#![feature(const_refs_to_cell)]
|
||||
#![feature(const_size_of_val)]
|
||||
#![feature(const_waker)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(core_panic)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_pin)]
|
||||
#![feature(const_waker)]
|
||||
#![feature(cstr_from_bytes_until_nul)]
|
||||
#![feature(dispatch_from_dyn)]
|
||||
#![feature(error_generic_member_access)]
|
||||
#![feature(error_in_core)]
|
||||
@@ -128,7 +130,6 @@
|
||||
#![feature(hasher_prefixfree_extras)]
|
||||
#![feature(inline_const)]
|
||||
#![feature(inplace_iteration)]
|
||||
#![cfg_attr(test, feature(is_sorted))]
|
||||
#![feature(iter_advance_by)]
|
||||
#![feature(iter_next_chunk)]
|
||||
#![feature(iter_repeat_n)]
|
||||
@@ -136,8 +137,6 @@
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(maybe_uninit_uninit_array)]
|
||||
#![feature(maybe_uninit_uninit_array_transpose)]
|
||||
#![cfg_attr(test, feature(new_uninit))]
|
||||
#![feature(nonnull_slice_from_raw_parts)]
|
||||
#![feature(pattern)]
|
||||
#![feature(pointer_byte_offsets)]
|
||||
#![feature(provide_any)]
|
||||
@@ -153,6 +152,7 @@
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(slice_ptr_len)]
|
||||
#![feature(slice_range)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(trusted_len)]
|
||||
@@ -163,40 +163,42 @@
|
||||
#![feature(unicode_internals)]
|
||||
#![feature(unsize)]
|
||||
#![feature(utf8_chunks)]
|
||||
#![feature(std_internals)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(not(test), feature(generator_trait))]
|
||||
#![cfg_attr(test, feature(panic_update_hook))]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![feature(allocator_internals)]
|
||||
#![feature(allow_internal_unstable)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(c_unwind)]
|
||||
#![feature(cfg_sanitize)]
|
||||
#![feature(const_deref)]
|
||||
#![feature(const_mut_refs)]
|
||||
#![feature(const_ptr_write)]
|
||||
#![feature(const_precise_live_drops)]
|
||||
#![feature(const_ptr_write)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_try)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![feature(fundamental)]
|
||||
#![cfg_attr(not(test), feature(generator_trait))]
|
||||
#![feature(hashmap_internals)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(multiple_supertrait_upcastable)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(rustc_allow_const_fn_unstable)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unsized_fn_params)]
|
||||
#![feature(c_unwind)]
|
||||
#![feature(with_negative_coherence)]
|
||||
#![cfg_attr(test, feature(panic_update_hook))]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Rustdoc features:
|
||||
#![feature(doc_cfg)]
|
||||
|
||||
+12
-6
@@ -6,7 +6,6 @@ use core::alloc::LayoutError;
|
||||
use core::cmp;
|
||||
use core::intrinsics;
|
||||
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
|
||||
use core::ops::Drop;
|
||||
use core::ptr::{self, NonNull, Unique};
|
||||
use core::slice;
|
||||
|
||||
@@ -274,10 +273,15 @@ impl<T, A: Allocator> RawVec<T, A> {
|
||||
if T::IS_ZST || self.cap == 0 {
|
||||
None
|
||||
} else {
|
||||
// We have an allocated chunk of memory, so we can bypass runtime
|
||||
// checks to get our current layout.
|
||||
// We could use Layout::array here which ensures the absence of isize and usize overflows
|
||||
// and could hypothetically handle differences between stride and size, but this memory
|
||||
// has already been allocated so we know it can't overflow and currently rust does not
|
||||
// support such types. So we can do better by skipping some checks and avoid an unwrap.
|
||||
let _: () = const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
|
||||
unsafe {
|
||||
let layout = Layout::array::<T>(self.cap).unwrap_unchecked();
|
||||
let align = mem::align_of::<T>();
|
||||
let size = mem::size_of::<T>().unchecked_mul(self.cap);
|
||||
let layout = Layout::from_size_align_unchecked(size, align);
|
||||
Some((self.ptr.cast().into(), layout))
|
||||
}
|
||||
}
|
||||
@@ -465,11 +469,13 @@ impl<T, A: Allocator> RawVec<T, A> {
|
||||
assert!(cap <= self.capacity(), "Tried to shrink to a larger capacity");
|
||||
|
||||
let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) };
|
||||
|
||||
// See current_memory() why this assert is here
|
||||
let _: () = const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
|
||||
let ptr = unsafe {
|
||||
// `Layout::array` cannot overflow here because it would have
|
||||
// overflowed earlier when capacity was larger.
|
||||
let new_layout = Layout::array::<T>(cap).unwrap_unchecked();
|
||||
let new_size = mem::size_of::<T>().unchecked_mul(cap);
|
||||
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
|
||||
self.alloc
|
||||
.shrink(ptr, layout, new_layout)
|
||||
.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?
|
||||
|
||||
+33
-10
@@ -784,6 +784,38 @@ impl<T, A: Allocator> BorrowMut<[T]> for Vec<T, A> {
|
||||
}
|
||||
}
|
||||
|
||||
// Specializable trait for implementing ToOwned::clone_into. This is
|
||||
// public in the crate and has the Allocator parameter so that
|
||||
// vec::clone_from use it too.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
pub(crate) trait SpecCloneIntoVec<T, A: Allocator> {
|
||||
fn clone_into(&self, target: &mut Vec<T, A>);
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl<T: Clone, A: Allocator> SpecCloneIntoVec<T, A> for [T] {
|
||||
default fn clone_into(&self, target: &mut Vec<T, A>) {
|
||||
// drop anything in target that will not be overwritten
|
||||
target.truncate(self.len());
|
||||
|
||||
// target.len <= self.len due to the truncate above, so the
|
||||
// slices here are always in-bounds.
|
||||
let (init, tail) = self.split_at(target.len());
|
||||
|
||||
// reuse the contained values' allocations/resources.
|
||||
target.clone_from_slice(init);
|
||||
target.extend_from_slice(tail);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl<T: Copy, A: Allocator> SpecCloneIntoVec<T, A> for [T] {
|
||||
fn clone_into(&self, target: &mut Vec<T, A>) {
|
||||
target.clear();
|
||||
target.extend_from_slice(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: Clone> ToOwned for [T] {
|
||||
@@ -799,16 +831,7 @@ impl<T: Clone> ToOwned for [T] {
|
||||
}
|
||||
|
||||
fn clone_into(&self, target: &mut Vec<T>) {
|
||||
// drop anything in target that will not be overwritten
|
||||
target.truncate(self.len());
|
||||
|
||||
// target.len <= self.len due to the truncate above, so the
|
||||
// slices here are always in-bounds.
|
||||
let (init, tail) = self.split_at(target.len());
|
||||
|
||||
// reuse the contained values' allocations/resources.
|
||||
target.clone_from_slice(init);
|
||||
target.extend_from_slice(tail);
|
||||
SpecCloneIntoVec::clone_into(self, target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use super::Vec;
|
||||
///
|
||||
/// ```
|
||||
/// let mut v = vec![0, 1, 2];
|
||||
/// let iter: std::vec::Drain<_> = v.drain(..);
|
||||
/// let iter: std::vec::Drain<'_, _> = v.drain(..);
|
||||
/// ```
|
||||
#[stable(feature = "drain", since = "1.6.0")]
|
||||
pub struct Drain<
|
||||
@@ -114,9 +114,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
|
||||
let unyielded_ptr = this.iter.as_slice().as_ptr();
|
||||
|
||||
// ZSTs have no identity, so we don't need to move them around.
|
||||
let needs_move = mem::size_of::<T>() != 0;
|
||||
|
||||
if needs_move {
|
||||
if !T::IS_ZST {
|
||||
let start_ptr = source_vec.as_mut_ptr().add(start);
|
||||
|
||||
// memmove back unyielded elements
|
||||
@@ -199,7 +197,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
|
||||
}
|
||||
}
|
||||
|
||||
let iter = mem::replace(&mut self.iter, (&mut []).iter());
|
||||
let iter = mem::take(&mut self.iter);
|
||||
let drop_len = iter.len();
|
||||
|
||||
let mut vec = self.vec;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
use crate::alloc::{Allocator, Global};
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
use core::mem::{ManuallyDrop, SizedTypeProperties};
|
||||
use core::ptr;
|
||||
use core::slice;
|
||||
|
||||
@@ -18,7 +18,7 @@ use super::Vec;
|
||||
/// #![feature(drain_filter)]
|
||||
///
|
||||
/// let mut v = vec![0, 1, 2];
|
||||
/// let iter: std::vec::DrainFilter<_, _> = v.drain_filter(|x| *x % 2 == 0);
|
||||
/// let iter: std::vec::DrainFilter<'_, _, _> = v.drain_filter(|x| *x % 2 == 0);
|
||||
/// ```
|
||||
#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
|
||||
#[derive(Debug)]
|
||||
@@ -98,9 +98,7 @@ where
|
||||
|
||||
unsafe {
|
||||
// ZSTs have no identity, so we don't need to move them around.
|
||||
let needs_move = mem::size_of::<T>() != 0;
|
||||
|
||||
if needs_move && this.idx < this.old_len && this.del > 0 {
|
||||
if !T::IS_ZST && this.idx < this.old_len && this.del > 0 {
|
||||
let ptr = this.vec.as_mut_ptr();
|
||||
let src = ptr.add(this.idx);
|
||||
let dst = src.sub(this.del);
|
||||
|
||||
+24
-11
@@ -13,6 +13,7 @@ use core::iter::{
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
|
||||
use core::num::NonZeroUsize;
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::ops::Deref;
|
||||
use core::ptr::{self, NonNull};
|
||||
@@ -109,7 +110,7 @@ impl<T, A: Allocator> IntoIter<T, A> {
|
||||
/// ```
|
||||
/// # let mut into_iter = Vec::<u8>::with_capacity(10).into_iter();
|
||||
/// let mut into_iter = std::mem::replace(&mut into_iter, Vec::new().into_iter());
|
||||
/// (&mut into_iter).for_each(core::mem::drop);
|
||||
/// (&mut into_iter).for_each(drop);
|
||||
/// std::mem::forget(into_iter);
|
||||
/// ```
|
||||
///
|
||||
@@ -215,7 +216,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
|
||||
fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> {
|
||||
let step_size = self.len().min(n);
|
||||
let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size);
|
||||
if T::IS_ZST {
|
||||
@@ -229,10 +230,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
|
||||
unsafe {
|
||||
ptr::drop_in_place(to_drop);
|
||||
}
|
||||
if step_size < n {
|
||||
return Err(step_size);
|
||||
}
|
||||
Ok(())
|
||||
NonZeroUsize::new(n - step_size).map_or(Ok(()), Err)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -315,7 +313,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
|
||||
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZeroUsize> {
|
||||
let step_size = self.len().min(n);
|
||||
if T::IS_ZST {
|
||||
// SAFETY: same as for advance_by()
|
||||
@@ -329,10 +327,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
|
||||
unsafe {
|
||||
ptr::drop_in_place(to_drop);
|
||||
}
|
||||
if step_size < n {
|
||||
return Err(step_size);
|
||||
}
|
||||
Ok(())
|
||||
NonZeroUsize::new(n - step_size).map_or(Ok(()), Err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +344,24 @@ impl<T, A: Allocator> FusedIterator for IntoIter<T, A> {}
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
unsafe impl<T, A: Allocator> TrustedLen for IntoIter<T, A> {}
|
||||
|
||||
#[stable(feature = "default_iters", since = "1.70.0")]
|
||||
impl<T, A> Default for IntoIter<T, A>
|
||||
where
|
||||
A: Allocator + Default,
|
||||
{
|
||||
/// Creates an empty `vec::IntoIter`.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::vec;
|
||||
/// let iter: vec::IntoIter<u8> = Default::default();
|
||||
/// assert_eq!(iter.len(), 0);
|
||||
/// assert_eq!(iter.as_slice(), &[]);
|
||||
/// ```
|
||||
fn default() -> Self {
|
||||
super::Vec::new_in(Default::default()).into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unstable(issue = "none", feature = "std_internals")]
|
||||
#[rustc_unsafe_specialization_marker]
|
||||
|
||||
+19
-65
@@ -58,13 +58,9 @@
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::cmp;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::intrinsics::assume;
|
||||
use core::iter;
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::iter::FromIterator;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
|
||||
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
|
||||
@@ -381,8 +377,8 @@ mod spec_extend;
|
||||
/// Currently, `Vec` does not guarantee the order in which elements are dropped.
|
||||
/// The order has changed in the past and may change again.
|
||||
///
|
||||
/// [`get`]: ../../std/vec/struct.Vec.html#method.get
|
||||
/// [`get_mut`]: ../../std/vec/struct.Vec.html#method.get_mut
|
||||
/// [`get`]: slice::get
|
||||
/// [`get_mut`]: slice::get_mut
|
||||
/// [`String`]: crate::string::String
|
||||
/// [`&str`]: type@str
|
||||
/// [`shrink_to_fit`]: Vec::shrink_to_fit
|
||||
@@ -708,14 +704,14 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
///
|
||||
/// // The vector contains no items, even though it has capacity for more
|
||||
/// assert_eq!(vec.len(), 0);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
///
|
||||
/// // These are all done without reallocating...
|
||||
/// for i in 0..10 {
|
||||
/// vec.push(i);
|
||||
/// }
|
||||
/// assert_eq!(vec.len(), 10);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
///
|
||||
/// // ...but this may make the vector reallocate
|
||||
/// vec.push(11);
|
||||
@@ -766,14 +762,14 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
///
|
||||
/// // The vector contains no items, even though it has capacity for more
|
||||
/// assert_eq!(vec.len(), 0);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
///
|
||||
/// // These are all done without reallocating...
|
||||
/// for i in 0..10 {
|
||||
/// vec.push(i);
|
||||
/// }
|
||||
/// assert_eq!(vec.len(), 10);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
///
|
||||
/// // ...but this may make the vector reallocate
|
||||
/// vec.push(11);
|
||||
@@ -999,7 +995,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
/// ```
|
||||
/// let mut vec: Vec<i32> = Vec::with_capacity(10);
|
||||
/// vec.push(42);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
@@ -1150,7 +1146,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
/// ```
|
||||
/// let mut vec = Vec::with_capacity(10);
|
||||
/// vec.extend([1, 2, 3]);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
/// vec.shrink_to_fit();
|
||||
/// assert!(vec.capacity() >= 3);
|
||||
/// ```
|
||||
@@ -1177,7 +1173,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
/// ```
|
||||
/// let mut vec = Vec::with_capacity(10);
|
||||
/// vec.extend([1, 2, 3]);
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
/// vec.shrink_to(4);
|
||||
/// assert!(vec.capacity() >= 4);
|
||||
/// vec.shrink_to(0);
|
||||
@@ -1212,7 +1208,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
/// let mut vec = Vec::with_capacity(10);
|
||||
/// vec.extend([1, 2, 3]);
|
||||
///
|
||||
/// assert_eq!(vec.capacity(), 10);
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
/// let slice = vec.into_boxed_slice();
|
||||
/// assert_eq!(slice.into_vec().capacity(), 3);
|
||||
/// ```
|
||||
@@ -1358,11 +1354,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
pub fn as_ptr(&self) -> *const T {
|
||||
// We shadow the slice method of the same name to avoid going through
|
||||
// `deref`, which creates an intermediate reference.
|
||||
let ptr = self.buf.ptr();
|
||||
unsafe {
|
||||
assume(!ptr.is_null());
|
||||
}
|
||||
ptr
|
||||
self.buf.ptr()
|
||||
}
|
||||
|
||||
/// Returns an unsafe mutable pointer to the vector's buffer, or a dangling
|
||||
@@ -1395,11 +1387,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
||||
// We shadow the slice method of the same name to avoid going through
|
||||
// `deref_mut`, which creates an intermediate reference.
|
||||
let ptr = self.buf.ptr();
|
||||
unsafe {
|
||||
assume(!ptr.is_null());
|
||||
}
|
||||
ptr
|
||||
self.buf.ptr()
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying allocator.
|
||||
@@ -2891,35 +2879,6 @@ impl<T, A: Allocator> ops::DerefMut for Vec<T, A> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
trait SpecCloneFrom {
|
||||
fn clone_from(this: &mut Self, other: &Self);
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl<T: Clone, A: Allocator> SpecCloneFrom for Vec<T, A> {
|
||||
default fn clone_from(this: &mut Self, other: &Self) {
|
||||
// drop anything that will not be overwritten
|
||||
this.truncate(other.len());
|
||||
|
||||
// self.len <= other.len due to the truncate above, so the
|
||||
// slices here are always in-bounds.
|
||||
let (init, tail) = other.split_at(this.len());
|
||||
|
||||
// reuse the contained values' allocations/resources.
|
||||
this.clone_from_slice(init);
|
||||
this.extend_from_slice(tail);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl<T: Copy, A: Allocator> SpecCloneFrom for Vec<T, A> {
|
||||
fn clone_from(this: &mut Self, other: &Self) {
|
||||
this.clear();
|
||||
this.extend_from_slice(other);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
|
||||
@@ -2940,7 +2899,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
|
||||
}
|
||||
|
||||
fn clone_from(&mut self, other: &Self) {
|
||||
SpecCloneFrom::clone_from(self, other)
|
||||
crate::slice::SpecCloneIntoVec::clone_into(other.as_slice(), self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2948,7 +2907,6 @@ impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
|
||||
/// as required by the `core::borrow::Borrow` implementation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(build_hasher_simple_hash_one)]
|
||||
/// use std::hash::BuildHasher;
|
||||
///
|
||||
/// let b = std::collections::hash_map::RandomState::new();
|
||||
@@ -3330,7 +3288,7 @@ impl<'a, T: Copy + 'a, A: Allocator + 'a> Extend<&'a T> for Vec<T, A> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements comparison of vectors, [lexicographically](core::cmp::Ord#lexicographical-comparison).
|
||||
/// Implements comparison of vectors, [lexicographically](Ord#lexicographical-comparison).
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: PartialOrd, A: Allocator> PartialOrd for Vec<T, A> {
|
||||
#[inline]
|
||||
@@ -3342,7 +3300,7 @@ impl<T: PartialOrd, A: Allocator> PartialOrd for Vec<T, A> {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: Eq, A: Allocator> Eq for Vec<T, A> {}
|
||||
|
||||
/// Implements ordering of vectors, [lexicographically](core::cmp::Ord#lexicographical-comparison).
|
||||
/// Implements ordering of vectors, [lexicographically](Ord#lexicographical-comparison).
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: Ord, A: Allocator> Ord for Vec<T, A> {
|
||||
#[inline]
|
||||
@@ -3365,8 +3323,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for Vec<T, A> {
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
|
||||
impl<T> const Default for Vec<T> {
|
||||
impl<T> Default for Vec<T> {
|
||||
/// Creates an empty `Vec<T>`.
|
||||
///
|
||||
/// The vector will not allocate until elements are pushed onto it.
|
||||
@@ -3462,10 +3419,7 @@ impl<T, const N: usize> From<[T; N]> for Vec<T> {
|
||||
/// ```
|
||||
#[cfg(not(test))]
|
||||
fn from(s: [T; N]) -> Vec<T> {
|
||||
<[T]>::into_vec(
|
||||
#[rustc_box]
|
||||
Box::new(s),
|
||||
)
|
||||
<[T]>::into_vec(Box::new(s))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -3490,8 +3444,8 @@ where
|
||||
///
|
||||
/// ```
|
||||
/// # use std::borrow::Cow;
|
||||
/// let o: Cow<[i32]> = Cow::Owned(vec![1, 2, 3]);
|
||||
/// let b: Cow<[i32]> = Cow::Borrowed(&[1, 2, 3]);
|
||||
/// let o: Cow<'_, [i32]> = Cow::Owned(vec![1, 2, 3]);
|
||||
/// let b: Cow<'_, [i32]> = Cow::Borrowed(&[1, 2, 3]);
|
||||
/// assert_eq!(Vec::from(o), Vec::from(b));
|
||||
/// ```
|
||||
fn from(s: Cow<'a, [T]>) -> Vec<T> {
|
||||
|
||||
@@ -37,14 +37,21 @@ macro_rules! define_panicking_intrinsics(
|
||||
);
|
||||
|
||||
define_panicking_intrinsics!("`f32` should not be used", {
|
||||
__addsf3,
|
||||
__eqsf2,
|
||||
__gesf2,
|
||||
__lesf2,
|
||||
__ltsf2,
|
||||
__mulsf3,
|
||||
__nesf2,
|
||||
__unordsf2,
|
||||
});
|
||||
|
||||
define_panicking_intrinsics!("`f64` should not be used", {
|
||||
__adddf3,
|
||||
__ledf2,
|
||||
__ltdf2,
|
||||
__muldf3,
|
||||
__unorddf2,
|
||||
});
|
||||
|
||||
|
||||
+11
-10
@@ -16,6 +16,8 @@
|
||||
*
|
||||
* All symbols are exported as GPL-only to guarantee no GPL-only feature is
|
||||
* accidentally exposed.
|
||||
*
|
||||
* Sorted alphabetically.
|
||||
*/
|
||||
|
||||
#include <kunit/test-bug.h>
|
||||
@@ -23,10 +25,10 @@
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errname.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
__noreturn void rust_helper_BUG(void)
|
||||
@@ -143,19 +145,18 @@ struct kunit *rust_helper_kunit_get_current_test(void)
|
||||
EXPORT_SYMBOL_GPL(rust_helper_kunit_get_current_test);
|
||||
|
||||
/*
|
||||
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
|
||||
* as the Rust `usize` type, so we can use it in contexts where Rust
|
||||
* expects a `usize` like slice (array) indices. `usize` is defined to be
|
||||
* the same as C's `uintptr_t` type (can hold any pointer) but not
|
||||
* necessarily the same as `size_t` (can hold the size of any single
|
||||
* object). Most modern platforms use the same concrete integer type for
|
||||
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
|
||||
* use it in contexts where Rust expects a `usize` like slice (array) indices.
|
||||
* `usize` is defined to be the same as C's `uintptr_t` type (can hold any
|
||||
* pointer) but not necessarily the same as `size_t` (can hold the size of any
|
||||
* single object). Most modern platforms use the same concrete integer type for
|
||||
* both of them, but in case we find ourselves on a platform where
|
||||
* that's not true, fail early instead of risking ABI or
|
||||
* integer-overflow issues.
|
||||
*
|
||||
* If your platform fails this assertion, it means that you are in
|
||||
* danger of integer-overflow bugs (even if you attempt to remove
|
||||
* `--size_t-is-usize`). It may be easiest to change the kernel ABI on
|
||||
* danger of integer-overflow bugs (even if you attempt to add
|
||||
* `--no-size_t-is-usize`). It may be easiest to change the kernel ABI on
|
||||
* your platform such that `size_t` matches `uintptr_t` (i.e., to increase
|
||||
* `size_t`, because `uintptr_t` has to be at least as big as `size_t`).
|
||||
*/
|
||||
|
||||
+32
-52
@@ -41,9 +41,9 @@ unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gf
|
||||
|
||||
unsafe impl GlobalAlloc for KernelAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
// `krealloc()` is used instead of `kmalloc()` because the latter is
|
||||
// an inline function and cannot be bound to as a result.
|
||||
unsafe { bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL) as *mut u8 }
|
||||
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
|
||||
// requirement.
|
||||
unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) }
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
@@ -51,58 +51,38 @@ unsafe impl GlobalAlloc for KernelAllocator {
|
||||
bindings::kfree(ptr as *const core::ffi::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||||
// SAFETY:
|
||||
// - `new_size`, when rounded up to the nearest multiple of `layout.align()`, will not
|
||||
// overflow `isize` by the function safety requirement.
|
||||
// - `layout.align()` is a proper alignment (i.e. not zero and must be a power of two).
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
|
||||
|
||||
// SAFETY:
|
||||
// - `ptr` is either null or a pointer allocated by this allocator by the function safety
|
||||
// requirement.
|
||||
// - the size of `layout` is not zero because `new_size` is not zero by the function safety
|
||||
// requirement.
|
||||
unsafe { krealloc_aligned(ptr, layout, bindings::GFP_KERNEL) }
|
||||
}
|
||||
|
||||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||||
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
|
||||
// requirement.
|
||||
unsafe {
|
||||
krealloc_aligned(
|
||||
ptr::null_mut(),
|
||||
layout,
|
||||
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: KernelAllocator = KernelAllocator;
|
||||
|
||||
// `rustc` only generates these for some crate types. Even then, we would need
|
||||
// to extract the object file that has them from the archive. For the moment,
|
||||
// let's generate them ourselves instead.
|
||||
//
|
||||
// Note: Although these are *safe* functions, they are called by the compiler
|
||||
// with parameters that obey the same `GlobalAlloc` function safety
|
||||
// requirements: size and align should form a valid layout, and size is
|
||||
// greater than 0.
|
||||
//
|
||||
// Note that `#[no_mangle]` implies exported too, nowadays.
|
||||
// See <https://github.com/rust-lang/rust/pull/86844>.
|
||||
#[no_mangle]
|
||||
fn __rust_alloc(size: usize, align: usize) -> *mut u8 {
|
||||
// SAFETY: See assumption above.
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
|
||||
|
||||
// SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater
|
||||
// than 0.
|
||||
unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn __rust_dealloc(ptr: *mut u8, _size: usize, _align: usize) {
|
||||
unsafe { bindings::kfree(ptr as *const core::ffi::c_void) };
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn __rust_realloc(ptr: *mut u8, _old_size: usize, align: usize, new_size: usize) -> *mut u8 {
|
||||
// SAFETY: See assumption above.
|
||||
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, align) };
|
||||
|
||||
// SAFETY: Per assumption above, `ptr` is allocated by `__rust_*` before, and the size of
|
||||
// `new_layout` is greater than 0.
|
||||
unsafe { krealloc_aligned(ptr, new_layout, bindings::GFP_KERNEL) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
|
||||
// SAFETY: See assumption above.
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
|
||||
|
||||
// SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater
|
||||
// than 0.
|
||||
unsafe {
|
||||
krealloc_aligned(
|
||||
ptr::null_mut(),
|
||||
layout,
|
||||
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
|
||||
)
|
||||
}
|
||||
}
|
||||
static __rust_no_alloc_shim_is_unstable: u8 = 0;
|
||||
|
||||
+275
-371
@@ -212,11 +212,12 @@
|
||||
use crate::{
|
||||
error::{self, Error},
|
||||
sync::UniqueArc,
|
||||
types::{Opaque, ScopeGuard},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
alloc::AllocError,
|
||||
cell::Cell,
|
||||
cell::UnsafeCell,
|
||||
convert::Infallible,
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
@@ -518,13 +519,17 @@ macro_rules! stack_try_pin_init {
|
||||
/// - Fields that you want to initialize in-place have to use `<-` instead of `:`.
|
||||
/// - In front of the initializer you can write `&this in` to have access to a [`NonNull<Self>`]
|
||||
/// pointer named `this` inside of the initializer.
|
||||
/// - Using struct update syntax one can place `..Zeroable::zeroed()` at the very end of the
|
||||
/// struct, this initializes every field with 0 and then runs all initializers specified in the
|
||||
/// body. This can only be done if [`Zeroable`] is implemented for the struct.
|
||||
///
|
||||
/// For instance:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use kernel::{macros::pin_data, pin_init};
|
||||
/// # use kernel::{macros::{Zeroable, pin_data}, pin_init};
|
||||
/// # use core::{ptr::addr_of_mut, marker::PhantomPinned};
|
||||
/// #[pin_data]
|
||||
/// #[derive(Zeroable)]
|
||||
/// struct Buf {
|
||||
/// // `ptr` points into `buf`.
|
||||
/// ptr: *mut u8,
|
||||
@@ -537,6 +542,10 @@ macro_rules! stack_try_pin_init {
|
||||
/// ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() },
|
||||
/// pin: PhantomPinned,
|
||||
/// });
|
||||
/// pin_init!(Buf {
|
||||
/// buf: [1; 64],
|
||||
/// ..Zeroable::zeroed()
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`try_pin_init!`]: kernel::try_pin_init
|
||||
@@ -548,11 +557,15 @@ macro_rules! pin_init {
|
||||
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
|
||||
$($fields:tt)*
|
||||
}) => {
|
||||
$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),
|
||||
@munch_fields($($fields)*),
|
||||
)
|
||||
};
|
||||
}
|
||||
@@ -601,205 +614,31 @@ macro_rules! try_pin_init {
|
||||
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
|
||||
$($fields:tt)*
|
||||
}) => {
|
||||
$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),
|
||||
@munch_fields($($fields)*),
|
||||
)
|
||||
};
|
||||
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
|
||||
$($fields:tt)*
|
||||
}? $err:ty) => {
|
||||
$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),
|
||||
@munch_fields($($fields)*),
|
||||
)
|
||||
};
|
||||
(
|
||||
@this($($this:ident)?),
|
||||
@typ($t:ident $(::<$($generics:ty),*>)?),
|
||||
@fields($($fields:tt)*),
|
||||
@error($err:ty),
|
||||
) => {{
|
||||
// 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 pin data from the supplied type.
|
||||
let data = unsafe {
|
||||
use $crate::init::__internal::HasPinData;
|
||||
$t$(::<$($generics),*>)?::__pin_data()
|
||||
};
|
||||
// Ensure that `data` really is of type `PinData` and help with type inference:
|
||||
let init = $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 user inside of the
|
||||
// expressions creating the individual fields.
|
||||
$(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(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 have been mentioned exactly
|
||||
// once, this struct initializer will still be type-checked and complain with a
|
||||
// very natural error message if a field is forgotten/mentioned more than once.
|
||||
#[allow(unreachable_code, clippy::diverging_sub_expression)]
|
||||
if false {
|
||||
$crate::try_pin_init!(make_initializer:
|
||||
@slot(slot),
|
||||
@type_name($t),
|
||||
@munch_fields($($fields)*,),
|
||||
@acc(),
|
||||
);
|
||||
}
|
||||
// Forget all guards, since initialization was a success.
|
||||
$crate::try_pin_init!(forget_guards:
|
||||
@munch_fields($($fields)*,),
|
||||
);
|
||||
}
|
||||
Ok(__InitOk)
|
||||
}
|
||||
);
|
||||
let init = move |slot| -> ::core::result::Result<(), $err> {
|
||||
init(slot).map(|__InitOk| ())
|
||||
};
|
||||
let init = unsafe { $crate::init::pin_init_from_closure::<_, $err>(init) };
|
||||
init
|
||||
}};
|
||||
(init_slot:
|
||||
@data($data:ident),
|
||||
@slot($slot:ident),
|
||||
@munch_fields($(,)?),
|
||||
) => {
|
||||
// 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)*),
|
||||
) => {
|
||||
let $field = $val;
|
||||
// Call the initializer.
|
||||
//
|
||||
// SAFETY: `slot` is valid, because we are inside of an initializer 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 succeeded.
|
||||
let $field = &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)*),
|
||||
) => {
|
||||
$(let $field = $val;)?
|
||||
// Initialize the field.
|
||||
//
|
||||
// SAFETY: The memory at `slot` is uninitialized.
|
||||
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
|
||||
// Create the drop guard:
|
||||
//
|
||||
// We only give access to `&DropGuard`, so it cannot be accidentally forgotten.
|
||||
//
|
||||
// SAFETY: We forget the guard later when initialization has succeeded.
|
||||
let $field = &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)*),
|
||||
) => {
|
||||
// Endpoint, nothing more to munch, create the initializer.
|
||||
// Since we are in the `if false` branch, this will never get executed. 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)*),
|
||||
) => {
|
||||
$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)*),
|
||||
) => {
|
||||
$crate::try_pin_init!(make_initializer:
|
||||
@slot($slot),
|
||||
@type_name($t),
|
||||
@munch_fields($($rest)*),
|
||||
@acc($($acc)* $field: ::core::panic!(),),
|
||||
);
|
||||
};
|
||||
(forget_guards:
|
||||
@munch_fields($(,)?),
|
||||
) => {
|
||||
// Munching finished.
|
||||
};
|
||||
(forget_guards:
|
||||
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
|
||||
) => {
|
||||
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)*),
|
||||
) => {
|
||||
unsafe { $crate::init::__internal::DropGuard::forget($field) };
|
||||
|
||||
$crate::try_pin_init!(forget_guards:
|
||||
@munch_fields($($rest)*),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Construct an in-place initializer for `struct`s.
|
||||
@@ -824,11 +663,15 @@ macro_rules! init {
|
||||
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
|
||||
$($fields:tt)*
|
||||
}) => {
|
||||
$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),
|
||||
@munch_fields($($fields)*),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -871,199 +714,31 @@ macro_rules! try_init {
|
||||
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
|
||||
$($fields:tt)*
|
||||
}) => {
|
||||
$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),
|
||||
@munch_fields($($fields)*),
|
||||
)
|
||||
};
|
||||
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
|
||||
$($fields:tt)*
|
||||
}? $err:ty) => {
|
||||
$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),
|
||||
@munch_fields($($fields)*),
|
||||
)
|
||||
};
|
||||
(
|
||||
@this($($this:ident)?),
|
||||
@typ($t:ident $(::<$($generics:ty),*>)?),
|
||||
@fields($($fields:tt)*),
|
||||
@error($err:ty),
|
||||
) => {{
|
||||
// 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 init data from the supplied type.
|
||||
let data = unsafe {
|
||||
use $crate::init::__internal::HasInitData;
|
||||
$t$(::<$($generics),*>)?::__init_data()
|
||||
};
|
||||
// Ensure that `data` really is of type `InitData` and help with type inference:
|
||||
let init = $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 user inside of the
|
||||
// expressions creating the individual fields.
|
||||
$(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)?
|
||||
// Initialize every field.
|
||||
$crate::try_init!(init_slot:
|
||||
@slot(slot),
|
||||
@munch_fields($($fields)*,),
|
||||
);
|
||||
// We use unreachable code to ensure that all fields have been mentioned exactly
|
||||
// once, this struct initializer will still be type-checked and complain with a
|
||||
// very natural error message if a field is forgotten/mentioned more than once.
|
||||
#[allow(unreachable_code, clippy::diverging_sub_expression)]
|
||||
if false {
|
||||
$crate::try_init!(make_initializer:
|
||||
@slot(slot),
|
||||
@type_name($t),
|
||||
@munch_fields($($fields)*,),
|
||||
@acc(),
|
||||
);
|
||||
}
|
||||
// Forget all guards, since initialization was a success.
|
||||
$crate::try_init!(forget_guards:
|
||||
@munch_fields($($fields)*,),
|
||||
);
|
||||
}
|
||||
Ok(__InitOk)
|
||||
}
|
||||
);
|
||||
let init = move |slot| -> ::core::result::Result<(), $err> {
|
||||
init(slot).map(|__InitOk| ())
|
||||
};
|
||||
let init = unsafe { $crate::init::init_from_closure::<_, $err>(init) };
|
||||
init
|
||||
}};
|
||||
(init_slot:
|
||||
@slot($slot:ident),
|
||||
@munch_fields( $(,)?),
|
||||
) => {
|
||||
// Endpoint of munching, no fields are left.
|
||||
};
|
||||
(init_slot:
|
||||
@slot($slot:ident),
|
||||
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
|
||||
) => {
|
||||
let $field = $val;
|
||||
// Call the initializer.
|
||||
//
|
||||
// SAFETY: `slot` is valid, because we are inside of an initializer 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 accidentally forgotten.
|
||||
//
|
||||
// SAFETY: We forget the guard later when initialization has succeeded.
|
||||
let $field = &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)*),
|
||||
) => {
|
||||
$(let $field = $val;)?
|
||||
// Call the initializer.
|
||||
//
|
||||
// SAFETY: The memory at `slot` is uninitialized.
|
||||
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
|
||||
// Create the drop guard.
|
||||
//
|
||||
// We only give access to `&DropGuard`, so it cannot be accidentally forgotten.
|
||||
//
|
||||
// SAFETY: We forget the guard later when initialization has succeeded.
|
||||
let $field = &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)*),
|
||||
) => {
|
||||
// Endpoint, nothing more to munch, create the initializer.
|
||||
// Since we are in the `if false` branch, this will never get executed. 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)*),
|
||||
) => {
|
||||
$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)*),
|
||||
) => {
|
||||
$crate::try_init!(make_initializer:
|
||||
@slot($slot),
|
||||
@type_name($t),
|
||||
@munch_fields($($rest)*),
|
||||
@acc($($acc)*$field: ::core::panic!(),),
|
||||
);
|
||||
};
|
||||
(forget_guards:
|
||||
@munch_fields($(,)?),
|
||||
) => {
|
||||
// Munching finished.
|
||||
};
|
||||
(forget_guards:
|
||||
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
|
||||
) => {
|
||||
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)*),
|
||||
) => {
|
||||
unsafe { $crate::init::__internal::DropGuard::forget($field) };
|
||||
|
||||
$crate::try_init!(forget_guards:
|
||||
@munch_fields($($rest)*),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// A pin-initializer for the type `T`.
|
||||
@@ -1100,6 +775,79 @@ pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized {
|
||||
/// deallocate.
|
||||
/// - `slot` will not move until it is dropped, i.e. it will be pinned.
|
||||
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E>;
|
||||
|
||||
/// First initializes the value using `self` then calls the function `f` with the initialized
|
||||
/// value.
|
||||
///
|
||||
/// If `f` returns an error the value is dropped and the initializer will forward the error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![allow(clippy::disallowed_names)]
|
||||
/// use kernel::{types::Opaque, init::pin_init_from_closure};
|
||||
/// #[repr(C)]
|
||||
/// struct RawFoo([u8; 16]);
|
||||
/// extern {
|
||||
/// fn init_foo(_: *mut RawFoo);
|
||||
/// }
|
||||
///
|
||||
/// #[pin_data]
|
||||
/// struct Foo {
|
||||
/// #[pin]
|
||||
/// raw: Opaque<RawFoo>,
|
||||
/// }
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn setup(self: Pin<&mut Self>) {
|
||||
/// pr_info!("Setting up foo");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let foo = pin_init!(Foo {
|
||||
/// raw <- unsafe {
|
||||
/// Opaque::ffi_init(|s| {
|
||||
/// init_foo(s);
|
||||
/// })
|
||||
/// },
|
||||
/// }).pin_chain(|foo| {
|
||||
/// foo.setup();
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
fn pin_chain<F>(self, f: F) -> ChainPinInit<Self, F, T, E>
|
||||
where
|
||||
F: FnOnce(Pin<&mut T>) -> Result<(), E>,
|
||||
{
|
||||
ChainPinInit(self, f, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// An initializer returned by [`PinInit::pin_chain`].
|
||||
pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
|
||||
|
||||
// SAFETY: The `__pinned_init` function is implemented such that it
|
||||
// - returns `Ok(())` on successful initialization,
|
||||
// - returns `Err(err)` on error and in this case `slot` will be dropped.
|
||||
// - considers `slot` pinned.
|
||||
unsafe impl<T: ?Sized, E, I, F> PinInit<T, E> for ChainPinInit<I, F, T, E>
|
||||
where
|
||||
I: PinInit<T, E>,
|
||||
F: FnOnce(Pin<&mut T>) -> Result<(), E>,
|
||||
{
|
||||
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
|
||||
// SAFETY: All requirements fulfilled since this function is `__pinned_init`.
|
||||
unsafe { self.0.__pinned_init(slot)? };
|
||||
// SAFETY: The above call initialized `slot` and we still have unique access.
|
||||
let val = unsafe { &mut *slot };
|
||||
// SAFETY: `slot` is considered pinned.
|
||||
let val = unsafe { Pin::new_unchecked(val) };
|
||||
(self.1)(val).map_err(|e| {
|
||||
// SAFETY: `slot` was initialized above.
|
||||
unsafe { core::ptr::drop_in_place(slot) };
|
||||
e
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An initializer for `T`.
|
||||
@@ -1132,7 +880,7 @@ pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized {
|
||||
///
|
||||
/// [`Arc<T>`]: crate::sync::Arc
|
||||
#[must_use = "An initializer must be used in order to create its value."]
|
||||
pub unsafe trait Init<T: ?Sized, E = Infallible>: Sized {
|
||||
pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
|
||||
/// Initializes `slot`.
|
||||
///
|
||||
/// # Safety
|
||||
@@ -1141,16 +889,73 @@ pub unsafe trait Init<T: ?Sized, E = Infallible>: Sized {
|
||||
/// - the caller does not touch `slot` when `Err` is returned, they are only permitted to
|
||||
/// deallocate.
|
||||
unsafe fn __init(self, slot: *mut T) -> Result<(), E>;
|
||||
|
||||
/// First initializes the value using `self` then calls the function `f` with the initialized
|
||||
/// value.
|
||||
///
|
||||
/// If `f` returns an error the value is dropped and the initializer will forward the error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![allow(clippy::disallowed_names)]
|
||||
/// use kernel::{types::Opaque, init::{self, init_from_closure}};
|
||||
/// struct Foo {
|
||||
/// buf: [u8; 1_000_000],
|
||||
/// }
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn setup(&mut self) {
|
||||
/// pr_info!("Setting up foo");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let foo = init!(Foo {
|
||||
/// buf <- init::zeroed()
|
||||
/// }).chain(|foo| {
|
||||
/// foo.setup();
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
fn chain<F>(self, f: F) -> ChainInit<Self, F, T, E>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Result<(), E>,
|
||||
{
|
||||
ChainInit(self, f, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Every in-place initializer can also be used as a pin-initializer.
|
||||
unsafe impl<T: ?Sized, E, I> PinInit<T, E> for I
|
||||
/// An initializer returned by [`Init::chain`].
|
||||
pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
|
||||
|
||||
// SAFETY: The `__init` function is implemented such that it
|
||||
// - returns `Ok(())` on successful initialization,
|
||||
// - returns `Err(err)` on error and in this case `slot` will be dropped.
|
||||
unsafe impl<T: ?Sized, E, I, F> Init<T, E> for ChainInit<I, F, T, E>
|
||||
where
|
||||
I: Init<T, E>,
|
||||
F: FnOnce(&mut T) -> Result<(), E>,
|
||||
{
|
||||
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
|
||||
// SAFETY: All requirements fulfilled since this function is `__init`.
|
||||
unsafe { self.0.__pinned_init(slot)? };
|
||||
// SAFETY: The above call initialized `slot` and we still have unique access.
|
||||
(self.1)(unsafe { &mut *slot }).map_err(|e| {
|
||||
// SAFETY: `slot` was initialized above.
|
||||
unsafe { core::ptr::drop_in_place(slot) };
|
||||
e
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `__pinned_init` behaves exactly the same as `__init`.
|
||||
unsafe impl<T: ?Sized, E, I, F> PinInit<T, E> for ChainInit<I, F, T, E>
|
||||
where
|
||||
I: Init<T, E>,
|
||||
F: FnOnce(&mut T) -> Result<(), E>,
|
||||
{
|
||||
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
|
||||
// SAFETY: `__init` meets the same requirements as `__pinned_init`, except that it does not
|
||||
// require `slot` to not move after init.
|
||||
// SAFETY: `__init` has less strict requirements compared to `__pinned_init`.
|
||||
unsafe { self.__init(slot) }
|
||||
}
|
||||
}
|
||||
@@ -1202,6 +1007,93 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
|
||||
unsafe { init_from_closure(|_| Ok(())) }
|
||||
}
|
||||
|
||||
/// Initializes an array by initializing each element via the provided initializer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use kernel::{error::Error, init::init_array_from_fn};
|
||||
/// let array: Box<[usize; 1_000]>= Box::init::<Error>(init_array_from_fn(|i| i)).unwrap();
|
||||
/// assert_eq!(array.len(), 1_000);
|
||||
/// ```
|
||||
pub fn init_array_from_fn<I, const N: usize, T, E>(
|
||||
mut make_init: impl FnMut(usize) -> I,
|
||||
) -> impl Init<[T; N], E>
|
||||
where
|
||||
I: Init<T, E>,
|
||||
{
|
||||
let init = move |slot: *mut [T; N]| {
|
||||
let slot = slot.cast::<T>();
|
||||
// Counts the number of initialized elements and when dropped drops that many elements from
|
||||
// `slot`.
|
||||
let mut init_count = ScopeGuard::new_with_data(0, |i| {
|
||||
// We now free every element that has been initialized before:
|
||||
// SAFETY: The loop initialized exactly the values from 0..i and since we
|
||||
// return `Err` below, the caller will consider the memory at `slot` as
|
||||
// uninitialized.
|
||||
unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(slot, i)) };
|
||||
});
|
||||
for i in 0..N {
|
||||
let init = make_init(i);
|
||||
// SAFETY: Since 0 <= `i` < N, it is still in bounds of `[T; N]`.
|
||||
let ptr = unsafe { slot.add(i) };
|
||||
// SAFETY: The pointer is derived from `slot` and thus satisfies the `__init`
|
||||
// requirements.
|
||||
unsafe { init.__init(ptr) }?;
|
||||
*init_count += 1;
|
||||
}
|
||||
init_count.dismiss();
|
||||
Ok(())
|
||||
};
|
||||
// SAFETY: The initializer above initializes every element of the array. On failure it drops
|
||||
// any initialized elements and returns `Err`.
|
||||
unsafe { init_from_closure(init) }
|
||||
}
|
||||
|
||||
/// Initializes an array by initializing each element via the provided initializer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use kernel::{sync::{Arc, Mutex}, init::pin_init_array_from_fn, new_mutex};
|
||||
/// let array: Arc<[Mutex<usize>; 1_000]>=
|
||||
/// Arc::pin_init(pin_init_array_from_fn(|i| new_mutex!(i))).unwrap();
|
||||
/// assert_eq!(array.len(), 1_000);
|
||||
/// ```
|
||||
pub fn pin_init_array_from_fn<I, const N: usize, T, E>(
|
||||
mut make_init: impl FnMut(usize) -> I,
|
||||
) -> impl PinInit<[T; N], E>
|
||||
where
|
||||
I: PinInit<T, E>,
|
||||
{
|
||||
let init = move |slot: *mut [T; N]| {
|
||||
let slot = slot.cast::<T>();
|
||||
// Counts the number of initialized elements and when dropped drops that many elements from
|
||||
// `slot`.
|
||||
let mut init_count = ScopeGuard::new_with_data(0, |i| {
|
||||
// We now free every element that has been initialized before:
|
||||
// SAFETY: The loop initialized exactly the values from 0..i and since we
|
||||
// return `Err` below, the caller will consider the memory at `slot` as
|
||||
// uninitialized.
|
||||
unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(slot, i)) };
|
||||
});
|
||||
for i in 0..N {
|
||||
let init = make_init(i);
|
||||
// SAFETY: Since 0 <= `i` < N, it is still in bounds of `[T; N]`.
|
||||
let ptr = unsafe { slot.add(i) };
|
||||
// SAFETY: The pointer is derived from `slot` and thus satisfies the `__init`
|
||||
// requirements.
|
||||
unsafe { init.__pinned_init(ptr) }?;
|
||||
*init_count += 1;
|
||||
}
|
||||
init_count.dismiss();
|
||||
Ok(())
|
||||
};
|
||||
// SAFETY: The initializer above initializes every element of the array. On failure it drops
|
||||
// any initialized elements and returns `Err`.
|
||||
unsafe { pin_init_from_closure(init) }
|
||||
}
|
||||
|
||||
// SAFETY: Every type can be initialized by-value.
|
||||
unsafe impl<T, E> Init<T, E> for T {
|
||||
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
|
||||
@@ -1210,6 +1102,13 @@ unsafe impl<T, E> Init<T, E> for T {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Every type can be initialized by-value. `__pinned_init` calls `__init`.
|
||||
unsafe impl<T, E> PinInit<T, E> for T {
|
||||
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
|
||||
unsafe { self.__init(slot) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Smart pointer that can initialize memory in-place.
|
||||
pub trait InPlaceInit<T>: Sized {
|
||||
/// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this
|
||||
@@ -1398,6 +1297,11 @@ impl_zeroable! {
|
||||
|
||||
// SAFETY: Type is allowed to take any value, including all zeros.
|
||||
{<T>} MaybeUninit<T>,
|
||||
// SAFETY: Type is allowed to take any value, including all zeros.
|
||||
{<T>} Opaque<T>,
|
||||
|
||||
// SAFETY: `T: Zeroable` and `UnsafeCell` is `repr(transparent)`.
|
||||
{<T: ?Sized + Zeroable>} UnsafeCell<T>,
|
||||
|
||||
// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
|
||||
Option<NonZeroU8>, Option<NonZeroU16>, Option<NonZeroU32>, Option<NonZeroU64>,
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::*;
|
||||
///
|
||||
/// [nomicon]: https://doc.rust-lang.org/nomicon/subtyping.html
|
||||
/// [this table]: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns
|
||||
type Invariant<T> = PhantomData<fn(*mut T) -> *mut T>;
|
||||
pub(super) type Invariant<T> = PhantomData<fn(*mut T) -> *mut T>;
|
||||
|
||||
/// This is the module-internal type implementing `PinInit` and `Init`. It is unsafe to create this
|
||||
/// type, since the closure needs to fulfill the same safety requirement as the
|
||||
@@ -32,6 +32,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the
|
||||
// `__pinned_init` invariants.
|
||||
unsafe impl<T: ?Sized, F, E> PinInit<T, E> for InitClosure<F, T, E>
|
||||
where
|
||||
F: FnOnce(*mut T) -> Result<(), E>,
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
|
||||
(self.0)(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is only implemented via the `#[pin_data]` proc-macro. It is used to facilitate
|
||||
/// the pin projections within the initializers.
|
||||
///
|
||||
@@ -174,7 +186,6 @@ impl<T> StackInit<T> {
|
||||
/// Can be forgotten to prevent the drop.
|
||||
pub struct DropGuard<T: ?Sized> {
|
||||
ptr: *mut T,
|
||||
do_drop: Cell<bool>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DropGuard<T> {
|
||||
@@ -190,32 +201,16 @@ impl<T: ?Sized> DropGuard<T> {
|
||||
/// - will not be dropped by any other means.
|
||||
#[inline]
|
||||
pub unsafe fn new(ptr: *mut T) -> Self {
|
||||
Self {
|
||||
ptr,
|
||||
do_drop: Cell::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prevents this guard from dropping the supplied pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe in order to prevent safe code from forgetting this guard. It should
|
||||
/// only be called by the macros in this module.
|
||||
#[inline]
|
||||
pub unsafe fn forget(&self) {
|
||||
self.do_drop.set(false);
|
||||
Self { ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for DropGuard<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.do_drop.get() {
|
||||
// SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
|
||||
// ensuring that this operation is safe.
|
||||
unsafe { ptr::drop_in_place(self.ptr) }
|
||||
}
|
||||
// SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
|
||||
// ensuring that this operation is safe.
|
||||
unsafe { ptr::drop_in_place(self.ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+460
-59
@@ -1,10 +1,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
//! This module provides the macros that actually implement the proc-macros `pin_data` and
|
||||
//! `pinned_drop`.
|
||||
//! `pinned_drop`. It also contains `__init_internal` the implementation of 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 macros 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.
|
||||
@@ -43,7 +45,7 @@
|
||||
//! #[pinned_drop]
|
||||
//! impl PinnedDrop for Foo {
|
||||
//! fn drop(self: Pin<&mut Self>) {
|
||||
//! println!("{self:p} is getting dropped.");
|
||||
//! pr_info!("{self:p} is getting dropped.");
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
@@ -168,8 +170,10 @@
|
||||
//! t: T,
|
||||
//! }
|
||||
//! #[doc(hidden)]
|
||||
//! impl<'__pin, T>
|
||||
//! ::core::marker::Unpin for Bar<T> where __Unpin<'__pin, T>: ::core::marker::Unpin {}
|
||||
//! impl<'__pin, T> ::core::marker::Unpin for Bar<T>
|
||||
//! where
|
||||
//! __Unpin<'__pin, T>: ::core::marker::Unpin,
|
||||
//! {}
|
||||
//! // Now we need to ensure that `Bar` does not implement `Drop`, since that would give users
|
||||
//! // access to `&mut self` inside of `drop` even if the struct was pinned. This could lead to
|
||||
//! // UB with only safe code, so we disallow this by giving a trait implementation error using
|
||||
@@ -186,8 +190,9 @@
|
||||
//! // for safety, but a good sanity check, since no normal code calls `PinnedDrop::drop`.
|
||||
//! #[allow(non_camel_case_types)]
|
||||
//! trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
|
||||
//! impl<T: ::kernel::init::PinnedDrop>
|
||||
//! UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
|
||||
//! impl<
|
||||
//! T: ::kernel::init::PinnedDrop,
|
||||
//! > UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
|
||||
//! impl<T> UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Bar<T> {}
|
||||
//! };
|
||||
//! ```
|
||||
@@ -217,7 +222,7 @@
|
||||
//! // 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 type from the initialized type.
|
||||
//! // Get the data about fields from the supplied type.
|
||||
//! // - the function is unsafe, hence the unsafe block
|
||||
//! // - we `use` the `HasPinData` trait in the block, it is only available in that
|
||||
//! // scope.
|
||||
@@ -225,8 +230,7 @@
|
||||
//! use ::kernel::init::__internal::HasPinData;
|
||||
//! Self::__pin_data()
|
||||
//! };
|
||||
//! // Use `data` to help with type inference, the closure supplied will have the type
|
||||
//! // `FnOnce(*mut Self) -> Result<__InitOk, Infallible>`.
|
||||
//! // Ensure that `data` really is of type `PinData` and help with type inference:
|
||||
//! let init = ::kernel::init::__internal::PinData::make_closure::<
|
||||
//! _,
|
||||
//! __InitOk,
|
||||
@@ -234,71 +238,75 @@
|
||||
//! >(data, move |slot| {
|
||||
//! {
|
||||
//! // Shadow the structure so it cannot be used to return early. If a user
|
||||
//! // tries to write `return Ok(__InitOk)`, then they get a type error, since
|
||||
//! // that will refer to this struct instead of the one defined above.
|
||||
//! // tries to write `return Ok(__InitOk)`, then they get a type error,
|
||||
//! // since that will refer to this struct instead of the one defined
|
||||
//! // above.
|
||||
//! struct __InitOk;
|
||||
//! // This is the expansion of `t,`, which is syntactic sugar for `t: t,`.
|
||||
//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).t), t) };
|
||||
//! // Since initialization could fail later (not in this case, since the error
|
||||
//! // type is `Infallible`) we will need to drop this field if there is an
|
||||
//! // error later. This `DropGuard` will drop the field when it gets dropped
|
||||
//! // and has not yet been forgotten. We make a reference to it, so users
|
||||
//! // cannot `mem::forget` it from the initializer, since the name is the same
|
||||
//! // as the field (including hygiene).
|
||||
//! let t = &unsafe {
|
||||
//! ::kernel::init::__internal::DropGuard::new(
|
||||
//! ::core::addr_of_mut!((*slot).t),
|
||||
//! )
|
||||
//! {
|
||||
//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).t), t) };
|
||||
//! }
|
||||
//! // Since initialization could fail later (not in this case, since the
|
||||
//! // error type is `Infallible`) we will need to drop this field if there
|
||||
//! // is an error later. This `DropGuard` will drop the field when it gets
|
||||
//! // dropped and has not yet been forgotten.
|
||||
//! let t = unsafe {
|
||||
//! ::pinned_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t))
|
||||
//! };
|
||||
//! // Expansion of `x: 0,`:
|
||||
//! // Since this can be an arbitrary expression we cannot place it inside of
|
||||
//! // the `unsafe` block, so we bind it here.
|
||||
//! let x = 0;
|
||||
//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).x), x) };
|
||||
//! // Since this can be an arbitrary expression we cannot place it inside
|
||||
//! // of the `unsafe` block, so we bind it here.
|
||||
//! {
|
||||
//! let x = 0;
|
||||
//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).x), x) };
|
||||
//! }
|
||||
//! // We again create a `DropGuard`.
|
||||
//! let x = &unsafe {
|
||||
//! ::kernel::init::__internal::DropGuard::new(
|
||||
//! ::core::addr_of_mut!((*slot).x),
|
||||
//! )
|
||||
//! let x = unsafe {
|
||||
//! ::kernel::init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).x))
|
||||
//! };
|
||||
//!
|
||||
//! // Since initialization has successfully completed, we can now forget
|
||||
//! // the guards. This is not `mem::forget`, since we only have
|
||||
//! // `&DropGuard`.
|
||||
//! ::core::mem::forget(x);
|
||||
//! ::core::mem::forget(t);
|
||||
//! // Here we use the type checker to ensure that every field has been
|
||||
//! // initialized exactly once, since this is `if false` it will never get
|
||||
//! // executed, but still type-checked.
|
||||
//! // Additionally we abuse `slot` to automatically infer the correct type for
|
||||
//! // the struct. This is also another check that every field is accessible
|
||||
//! // from this scope.
|
||||
//! // Additionally we abuse `slot` to automatically infer the correct type
|
||||
//! // for the struct. This is also another check that every field is
|
||||
//! // accessible from this scope.
|
||||
//! #[allow(unreachable_code, clippy::diverging_sub_expression)]
|
||||
//! if false {
|
||||
//! let _ = || {
|
||||
//! unsafe {
|
||||
//! ::core::ptr::write(
|
||||
//! slot,
|
||||
//! Self {
|
||||
//! // We only care about typecheck finding every field here,
|
||||
//! // the expression does not matter, just conjure one using
|
||||
//! // `panic!()`:
|
||||
//! // We only care about typecheck finding every field
|
||||
//! // here, the expression does not matter, just conjure
|
||||
//! // one using `panic!()`:
|
||||
//! t: ::core::panic!(),
|
||||
//! x: ::core::panic!(),
|
||||
//! },
|
||||
//! );
|
||||
//! };
|
||||
//! }
|
||||
//! // Since initialization has successfully completed, we can now forget the
|
||||
//! // guards. This is not `mem::forget`, since we only have `&DropGuard`.
|
||||
//! unsafe { ::kernel::init::__internal::DropGuard::forget(t) };
|
||||
//! unsafe { ::kernel::init::__internal::DropGuard::forget(x) };
|
||||
//! };
|
||||
//! }
|
||||
//! // We leave the scope above and gain access to the previously shadowed
|
||||
//! // `__InitOk` that we need to return.
|
||||
//! Ok(__InitOk)
|
||||
//! });
|
||||
//! // Change the return type from `__InitOk` to `()`.
|
||||
//! let init = move |slot| -> ::core::result::Result<(), ::core::convert::Infallible> {
|
||||
//! let init = move |
|
||||
//! slot,
|
||||
//! | -> ::core::result::Result<(), ::core::convert::Infallible> {
|
||||
//! init(slot).map(|__InitOk| ())
|
||||
//! };
|
||||
//! // Construct the initializer.
|
||||
//! let init = unsafe {
|
||||
//! ::kernel::init::pin_init_from_closure::<_, ::core::convert::Infallible>(init)
|
||||
//! ::kernel::init::pin_init_from_closure::<
|
||||
//! _,
|
||||
//! ::core::convert::Infallible,
|
||||
//! >(init)
|
||||
//! };
|
||||
//! init
|
||||
//! }
|
||||
@@ -372,7 +380,10 @@
|
||||
//! b: Bar<u32>,
|
||||
//! }
|
||||
//! #[doc(hidden)]
|
||||
//! impl<'__pin> ::core::marker::Unpin for Foo where __Unpin<'__pin>: ::core::marker::Unpin {}
|
||||
//! impl<'__pin> ::core::marker::Unpin for Foo
|
||||
//! where
|
||||
//! __Unpin<'__pin>: ::core::marker::Unpin,
|
||||
//! {}
|
||||
//! // Since we specified `PinnedDrop` as the argument to `#[pin_data]`, we expect `Foo` to
|
||||
//! // implement `PinnedDrop`. Thus we do not need to prevent `Drop` implementations like
|
||||
//! // before, instead we implement `Drop` here and delegate to `PinnedDrop`.
|
||||
@@ -401,7 +412,7 @@
|
||||
//! #[pinned_drop]
|
||||
//! impl PinnedDrop for Foo {
|
||||
//! fn drop(self: Pin<&mut Self>) {
|
||||
//! println!("{self:p} is getting dropped.");
|
||||
//! pr_info!("{self:p} is getting dropped.");
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
@@ -412,7 +423,7 @@
|
||||
//! // `unsafe`, full path and the token parameter are added, everything else stays the same.
|
||||
//! unsafe impl ::kernel::init::PinnedDrop for Foo {
|
||||
//! fn drop(self: Pin<&mut Self>, _: ::kernel::init::__internal::OnlyCallFromDrop) {
|
||||
//! println!("{self:p} is getting dropped.");
|
||||
//! pr_info!("{self:p} is getting dropped.");
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
@@ -447,18 +458,21 @@
|
||||
//! >(data, move |slot| {
|
||||
//! {
|
||||
//! struct __InitOk;
|
||||
//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).a), a) };
|
||||
//! let a = &unsafe {
|
||||
//! {
|
||||
//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).a), a) };
|
||||
//! }
|
||||
//! let a = unsafe {
|
||||
//! ::kernel::init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).a))
|
||||
//! };
|
||||
//! let b = Bar::new(36);
|
||||
//! let init = Bar::new(36);
|
||||
//! unsafe { data.b(::core::addr_of_mut!((*slot).b), b)? };
|
||||
//! let b = &unsafe {
|
||||
//! let b = unsafe {
|
||||
//! ::kernel::init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).b))
|
||||
//! };
|
||||
//!
|
||||
//! ::core::mem::forget(b);
|
||||
//! ::core::mem::forget(a);
|
||||
//! #[allow(unreachable_code, clippy::diverging_sub_expression)]
|
||||
//! if false {
|
||||
//! let _ = || {
|
||||
//! unsafe {
|
||||
//! ::core::ptr::write(
|
||||
//! slot,
|
||||
@@ -468,13 +482,13 @@
|
||||
//! },
|
||||
//! );
|
||||
//! };
|
||||
//! }
|
||||
//! unsafe { ::kernel::init::__internal::DropGuard::forget(a) };
|
||||
//! unsafe { ::kernel::init::__internal::DropGuard::forget(b) };
|
||||
//! };
|
||||
//! }
|
||||
//! Ok(__InitOk)
|
||||
//! });
|
||||
//! let init = move |slot| -> ::core::result::Result<(), ::core::convert::Infallible> {
|
||||
//! let init = move |
|
||||
//! slot,
|
||||
//! | -> ::core::result::Result<(), ::core::convert::Infallible> {
|
||||
//! init(slot).map(|__InitOk| ())
|
||||
//! };
|
||||
//! let init = unsafe {
|
||||
@@ -960,6 +974,7 @@ macro_rules! __pin_data {
|
||||
where $($whr)*
|
||||
{
|
||||
$(
|
||||
$(#[$($p_attr)*])*
|
||||
$pvis unsafe fn $p_field<E>(
|
||||
self,
|
||||
slot: *mut $p_type,
|
||||
@@ -969,6 +984,7 @@ macro_rules! __pin_data {
|
||||
}
|
||||
)*
|
||||
$(
|
||||
$(#[$($attr)*])*
|
||||
$fvis unsafe fn $field<E>(
|
||||
self,
|
||||
slot: *mut $type,
|
||||
@@ -980,3 +996,388 @@ macro_rules! __pin_data {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// - `with_update_parsed`: when the `..Zeroable::zeroed()` syntax has been handled.
|
||||
/// - `init_slot`: recursively creates the code that initializes all fields in `slot`.
|
||||
/// - `make_initializer`: recursively create the struct initializer that guarantees that every
|
||||
/// field has been initialized exactly once.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __init_internal {
|
||||
(
|
||||
@this($($this:ident)?),
|
||||
@typ($t:path),
|
||||
@fields($($fields:tt)*),
|
||||
@error($err:ty),
|
||||
// Either `PinData` or `InitData`, `$use_data` should only be present 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),
|
||||
@munch_fields(),
|
||||
) => {
|
||||
$crate::__init_internal!(with_update_parsed:
|
||||
@this($($this)?),
|
||||
@typ($t),
|
||||
@fields($($fields)*),
|
||||
@error($err),
|
||||
@data($data, $($use_data)?),
|
||||
@has_data($has_data, $get_data),
|
||||
@construct_closure($construct_closure),
|
||||
@zeroed(), // Nothing means default behavior.
|
||||
)
|
||||
};
|
||||
(
|
||||
@this($($this:ident)?),
|
||||
@typ($t:path),
|
||||
@fields($($fields:tt)*),
|
||||
@error($err:ty),
|
||||
// Either `PinData` or `InitData`, `$use_data` should only be present 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),
|
||||
@munch_fields(..Zeroable::zeroed()),
|
||||
) => {
|
||||
$crate::__init_internal!(with_update_parsed:
|
||||
@this($($this)?),
|
||||
@typ($t),
|
||||
@fields($($fields)*),
|
||||
@error($err),
|
||||
@data($data, $($use_data)?),
|
||||
@has_data($has_data, $get_data),
|
||||
@construct_closure($construct_closure),
|
||||
@zeroed(()), // `()` means zero all fields not mentioned.
|
||||
)
|
||||
};
|
||||
(
|
||||
@this($($this:ident)?),
|
||||
@typ($t:path),
|
||||
@fields($($fields:tt)*),
|
||||
@error($err:ty),
|
||||
// Either `PinData` or `InitData`, `$use_data` should only be present 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),
|
||||
@munch_fields($ignore:tt $($rest:tt)*),
|
||||
) => {
|
||||
$crate::__init_internal!(
|
||||
@this($($this)?),
|
||||
@typ($t),
|
||||
@fields($($fields)*),
|
||||
@error($err),
|
||||
@data($data, $($use_data)?),
|
||||
@has_data($has_data, $get_data),
|
||||
@construct_closure($construct_closure),
|
||||
@munch_fields($($rest)*),
|
||||
)
|
||||
};
|
||||
(with_update_parsed:
|
||||
@this($($this:ident)?),
|
||||
@typ($t:path),
|
||||
@fields($($fields:tt)*),
|
||||
@error($err:ty),
|
||||
// Either `PinData` or `InitData`, `$use_data` should only be present 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),
|
||||
@zeroed($($init_zeroed:expr)?),
|
||||
) => {{
|
||||
// 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.
|
||||
let data = unsafe {
|
||||
use $crate::init::__internal::$has_data;
|
||||
// Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal
|
||||
// information that is associated to already parsed fragments, so a path fragment
|
||||
// cannot be used in this position. Doing the retokenization results in valid rust
|
||||
// code.
|
||||
::kernel::macros::paste!($t::$get_data())
|
||||
};
|
||||
// Ensure that `data` really is of type `$data` and help with type inference:
|
||||
let init = $crate::init::__internal::$data::make_closure::<_, __InitOk, $err>(
|
||||
data,
|
||||
move |slot| {
|
||||
{
|
||||
// Shadow the structure so it cannot be used to return early.
|
||||
struct __InitOk;
|
||||
// If `$init_zeroed` is present we should zero the slot now and not emit an
|
||||
// error when fields are missing (since they will be zeroed). We also have to
|
||||
// check that the type actually implements `Zeroable`.
|
||||
$({
|
||||
fn assert_zeroable<T: $crate::init::Zeroable>(_: *mut T) {}
|
||||
// Ensure that the struct is indeed `Zeroable`.
|
||||
assert_zeroable(slot);
|
||||
// SAFETY: The type implements `Zeroable` by the check above.
|
||||
unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
|
||||
$init_zeroed // This will be `()` if set.
|
||||
})?
|
||||
// Create the `this` so it can be referenced by the user inside of the
|
||||
// expressions creating the individual fields.
|
||||
$(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)?
|
||||
// Initialize every field.
|
||||
$crate::__init_internal!(init_slot($($use_data)?):
|
||||
@data(data),
|
||||
@slot(slot),
|
||||
@guards(),
|
||||
@munch_fields($($fields)*,),
|
||||
);
|
||||
// We use unreachable code to ensure that all fields have been mentioned exactly
|
||||
// once, this struct initializer will still be type-checked and complain with a
|
||||
// very natural error message if a field is forgotten/mentioned more than once.
|
||||
#[allow(unreachable_code, clippy::diverging_sub_expression)]
|
||||
let _ = || {
|
||||
$crate::__init_internal!(make_initializer:
|
||||
@slot(slot),
|
||||
@type_name($t),
|
||||
@munch_fields($($fields)*,),
|
||||
@acc(),
|
||||
);
|
||||
};
|
||||
}
|
||||
Ok(__InitOk)
|
||||
}
|
||||
);
|
||||
let init = move |slot| -> ::core::result::Result<(), $err> {
|
||||
init(slot).map(|__InitOk| ())
|
||||
};
|
||||
let init = unsafe { $crate::init::$construct_closure::<_, $err>(init) };
|
||||
init
|
||||
}};
|
||||
(init_slot($($use_data:ident)?):
|
||||
@data($data:ident),
|
||||
@slot($slot:ident),
|
||||
@guards($($guards:ident,)*),
|
||||
@munch_fields($(..Zeroable::zeroed())? $(,)?),
|
||||
) => {
|
||||
// Endpoint of munching, no fields are left. If execution reaches this point, all fields
|
||||
// have been initialized. Therefore we can now dismiss the guards by forgetting them.
|
||||
$(::core::mem::forget($guards);)*
|
||||
};
|
||||
(init_slot($use_data:ident): // `use_data` is present, so we use the `data` to init fields.
|
||||
@data($data:ident),
|
||||
@slot($slot:ident),
|
||||
@guards($($guards:ident,)*),
|
||||
// In-place initialization syntax.
|
||||
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
|
||||
) => {
|
||||
let init = $val;
|
||||
// Call the initializer.
|
||||
//
|
||||
// SAFETY: `slot` is valid, because we are inside of an initializer 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), init)? };
|
||||
// Create the drop guard:
|
||||
//
|
||||
// We rely on macro hygiene to make it impossible for users to access this local variable.
|
||||
// We use `paste!` to create new hygiene for `$field`.
|
||||
::kernel::macros::paste! {
|
||||
// SAFETY: We forget the guard later when initialization has succeeded.
|
||||
let [<$field>] = unsafe {
|
||||
$crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
|
||||
};
|
||||
|
||||
$crate::__init_internal!(init_slot($use_data):
|
||||
@data($data),
|
||||
@slot($slot),
|
||||
@guards([<$field>], $($guards,)*),
|
||||
@munch_fields($($rest)*),
|
||||
);
|
||||
}
|
||||
};
|
||||
(init_slot(): // No `use_data`, so we use `Init::__init` directly.
|
||||
@data($data:ident),
|
||||
@slot($slot:ident),
|
||||
@guards($($guards:ident,)*),
|
||||
// In-place initialization syntax.
|
||||
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
|
||||
) => {
|
||||
let init = $val;
|
||||
// Call the initializer.
|
||||
//
|
||||
// SAFETY: `slot` is valid, because we are inside of an initializer closure, we
|
||||
// return when an error/panic occurs.
|
||||
unsafe { $crate::init::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? };
|
||||
// Create the drop guard:
|
||||
//
|
||||
// We rely on macro hygiene to make it impossible for users to access this local variable.
|
||||
// We use `paste!` to create new hygiene for `$field`.
|
||||
::kernel::macros::paste! {
|
||||
// SAFETY: We forget the guard later when initialization has succeeded.
|
||||
let [<$field>] = unsafe {
|
||||
$crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
|
||||
};
|
||||
|
||||
$crate::__init_internal!(init_slot():
|
||||
@data($data),
|
||||
@slot($slot),
|
||||
@guards([<$field>], $($guards,)*),
|
||||
@munch_fields($($rest)*),
|
||||
);
|
||||
}
|
||||
};
|
||||
(init_slot($($use_data:ident)?):
|
||||
@data($data:ident),
|
||||
@slot($slot:ident),
|
||||
@guards($($guards:ident,)*),
|
||||
// Init by-value.
|
||||
@munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
|
||||
) => {
|
||||
{
|
||||
$(let $field = $val;)?
|
||||
// Initialize the field.
|
||||
//
|
||||
// SAFETY: The memory at `slot` is uninitialized.
|
||||
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
|
||||
}
|
||||
// Create the drop guard:
|
||||
//
|
||||
// We rely on macro hygiene to make it impossible for users to access this local variable.
|
||||
// We use `paste!` to create new hygiene for `$field`.
|
||||
::kernel::macros::paste! {
|
||||
// SAFETY: We forget the guard later when initialization has succeeded.
|
||||
let [<$field>] = unsafe {
|
||||
$crate::init::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
|
||||
};
|
||||
|
||||
$crate::__init_internal!(init_slot($($use_data)?):
|
||||
@data($data),
|
||||
@slot($slot),
|
||||
@guards([<$field>], $($guards,)*),
|
||||
@munch_fields($($rest)*),
|
||||
);
|
||||
}
|
||||
};
|
||||
(make_initializer:
|
||||
@slot($slot:ident),
|
||||
@type_name($t:path),
|
||||
@munch_fields(..Zeroable::zeroed() $(,)?),
|
||||
@acc($($acc:tt)*),
|
||||
) => {
|
||||
// Endpoint, nothing more to munch, create the initializer. Since the users specified
|
||||
// `..Zeroable::zeroed()`, the slot will already have been zeroed and all field that have
|
||||
// not been overwritten are thus zero and initialized. We still check that all fields are
|
||||
// actually accessible by using the struct update syntax ourselves.
|
||||
// We are inside of a closure that is never executed and thus we can abuse `slot` to
|
||||
// get the correct type inference here:
|
||||
#[allow(unused_assignments)]
|
||||
unsafe {
|
||||
let mut zeroed = ::core::mem::zeroed();
|
||||
// We have to use type inference here to make zeroed have the correct type. This does
|
||||
// not get executed, so it has no effect.
|
||||
::core::ptr::write($slot, zeroed);
|
||||
zeroed = ::core::mem::zeroed();
|
||||
// Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal
|
||||
// information that is associated to already parsed fragments, so a path fragment
|
||||
// cannot be used in this position. Doing the retokenization results in valid rust
|
||||
// code.
|
||||
::kernel::macros::paste!(
|
||||
::core::ptr::write($slot, $t {
|
||||
$($acc)*
|
||||
..zeroed
|
||||
});
|
||||
);
|
||||
}
|
||||
};
|
||||
(make_initializer:
|
||||
@slot($slot:ident),
|
||||
@type_name($t:path),
|
||||
@munch_fields($(,)?),
|
||||
@acc($($acc:tt)*),
|
||||
) => {
|
||||
// Endpoint, nothing more to munch, create the initializer.
|
||||
// Since we are in the closure that is never called, this will never get executed.
|
||||
// We abuse `slot` to get the correct type inference here:
|
||||
unsafe {
|
||||
// Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal
|
||||
// information that is associated to already parsed fragments, so a path fragment
|
||||
// cannot be used in this position. Doing the retokenization results in valid rust
|
||||
// code.
|
||||
::kernel::macros::paste!(
|
||||
::core::ptr::write($slot, $t {
|
||||
$($acc)*
|
||||
});
|
||||
);
|
||||
}
|
||||
};
|
||||
(make_initializer:
|
||||
@slot($slot:ident),
|
||||
@type_name($t:path),
|
||||
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
|
||||
@acc($($acc:tt)*),
|
||||
) => {
|
||||
$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:path),
|
||||
@munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
|
||||
@acc($($acc:tt)*),
|
||||
) => {
|
||||
$crate::__init_internal!(make_initializer:
|
||||
@slot($slot),
|
||||
@type_name($t),
|
||||
@munch_fields($($rest)*),
|
||||
@acc($($acc)* $field: ::core::panic!(),),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __derive_zeroable {
|
||||
(parse_input:
|
||||
@sig(
|
||||
$(#[$($struct_attr:tt)*])*
|
||||
$vis:vis struct $name:ident
|
||||
$(where $($whr:tt)*)?
|
||||
),
|
||||
@impl_generics($($impl_generics:tt)*),
|
||||
@ty_generics($($ty_generics:tt)*),
|
||||
@body({
|
||||
$(
|
||||
$(#[$($field_attr:tt)*])*
|
||||
$field:ident : $field_ty:ty
|
||||
),* $(,)?
|
||||
}),
|
||||
) => {
|
||||
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
|
||||
#[automatically_derived]
|
||||
unsafe impl<$($impl_generics)*> $crate::init::Zeroable for $name<$($ty_generics)*>
|
||||
where
|
||||
$($($whr)*)?
|
||||
{}
|
||||
const _: () = {
|
||||
fn assert_zeroable<T: ?::core::marker::Sized + $crate::init::Zeroable>() {}
|
||||
fn ensure_zeroable<$($impl_generics)*>()
|
||||
where $($($whr)*)?
|
||||
{
|
||||
$(assert_zeroable::<$field_ty>();)*
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,7 +95,4 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
|
||||
pr_emerg!("{}\n", info);
|
||||
// SAFETY: FFI call.
|
||||
unsafe { bindings::BUG() };
|
||||
// Bindgen currently does not recognize `__noreturn` so `BUG` returns `()`
|
||||
// instead of `!`. See <https://github.com/rust-lang/rust-bindgen/issues/2094>.
|
||||
loop {}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub use core::pin::Pin;
|
||||
pub use alloc::{boxed::Box, vec::Vec};
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use macros::{module, pin_data, pinned_drop, vtable};
|
||||
pub use macros::{module, pin_data, pinned_drop, vtable, Zeroable};
|
||||
|
||||
pub use super::build_assert;
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ pub unsafe trait Backend {
|
||||
|
||||
/// A mutual exclusion primitive.
|
||||
///
|
||||
/// Exposes one of the kernel locking primitives. Which one is exposed depends on the lock backend
|
||||
/// specified as the generic parameter `B`.
|
||||
/// Exposes one of the kernel locking primitives. Which one is exposed depends on the lock
|
||||
/// [`Backend`] specified as the generic parameter `B`.
|
||||
#[pin_data]
|
||||
pub struct Lock<T: ?Sized, B: Backend> {
|
||||
/// The kernel lock object.
|
||||
@@ -126,7 +126,7 @@ impl<T: ?Sized, B: Backend> Lock<T, B> {
|
||||
|
||||
/// A lock guard.
|
||||
///
|
||||
/// Allows mutual exclusion primitives that implement the `Backend` trait to automatically unlock
|
||||
/// Allows mutual exclusion primitives that implement the [`Backend`] trait to automatically unlock
|
||||
/// when a guard goes out of scope. It also provides a safe and convenient way to access the data
|
||||
/// protected by the lock.
|
||||
#[must_use = "the lock unlocks immediately when the guard is unused"]
|
||||
|
||||
+15
-6
@@ -6,7 +6,7 @@ use crate::init::{self, PinInit};
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
marker::PhantomData,
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
@@ -206,17 +206,26 @@ impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> {
|
||||
///
|
||||
/// This is meant to be used with FFI objects that are never interpreted by Rust code.
|
||||
#[repr(transparent)]
|
||||
pub struct Opaque<T>(MaybeUninit<UnsafeCell<T>>);
|
||||
pub struct Opaque<T> {
|
||||
value: UnsafeCell<MaybeUninit<T>>,
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
|
||||
impl<T> Opaque<T> {
|
||||
/// Creates a new opaque value.
|
||||
pub const fn new(value: T) -> Self {
|
||||
Self(MaybeUninit::new(UnsafeCell::new(value)))
|
||||
Self {
|
||||
value: UnsafeCell::new(MaybeUninit::new(value)),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an uninitialised value.
|
||||
pub const fn uninit() -> Self {
|
||||
Self(MaybeUninit::uninit())
|
||||
Self {
|
||||
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a pin-initializer from the given initializer closure.
|
||||
@@ -240,7 +249,7 @@ impl<T> Opaque<T> {
|
||||
|
||||
/// Returns a raw pointer to the opaque data.
|
||||
pub fn get(&self) -> *mut T {
|
||||
UnsafeCell::raw_get(self.0.as_ptr())
|
||||
UnsafeCell::get(&self.value).cast::<T>()
|
||||
}
|
||||
|
||||
/// Gets the value behind `this`.
|
||||
@@ -248,7 +257,7 @@ impl<T> Opaque<T> {
|
||||
/// This function is useful to get access to the value without creating intermediate
|
||||
/// references.
|
||||
pub const fn raw_get(this: *const Self) -> *mut T {
|
||||
UnsafeCell::raw_get(this.cast::<UnsafeCell<T>>())
|
||||
UnsafeCell::raw_get(this.cast::<UnsafeCell<MaybeUninit<T>>>()).cast::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ mod quote;
|
||||
mod concat_idents;
|
||||
mod helpers;
|
||||
mod module;
|
||||
mod paste;
|
||||
mod pin_data;
|
||||
mod pinned_drop;
|
||||
mod vtable;
|
||||
mod zeroable;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
@@ -246,3 +248,118 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
|
||||
pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pinned_drop::pinned_drop(args, input)
|
||||
}
|
||||
|
||||
/// Paste identifiers together.
|
||||
///
|
||||
/// Within the `paste!` macro, identifiers inside `[<` and `>]` are concatenated together to form a
|
||||
/// single identifier.
|
||||
///
|
||||
/// This is similar to the [`paste`] crate, but with pasting feature limited to identifiers
|
||||
/// (literals, lifetimes and documentation strings are not supported). There is a difference in
|
||||
/// supported modifiers as well.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use kernel::macro::paste;
|
||||
///
|
||||
/// macro_rules! pub_no_prefix {
|
||||
/// ($prefix:ident, $($newname:ident),+) => {
|
||||
/// paste! {
|
||||
/// $(pub(crate) const $newname: u32 = [<$prefix $newname>];)+
|
||||
/// }
|
||||
/// };
|
||||
/// }
|
||||
///
|
||||
/// pub_no_prefix!(
|
||||
/// binder_driver_return_protocol_,
|
||||
/// BR_OK,
|
||||
/// BR_ERROR,
|
||||
/// BR_TRANSACTION,
|
||||
/// BR_REPLY,
|
||||
/// BR_DEAD_REPLY,
|
||||
/// BR_TRANSACTION_COMPLETE,
|
||||
/// BR_INCREFS,
|
||||
/// BR_ACQUIRE,
|
||||
/// BR_RELEASE,
|
||||
/// BR_DECREFS,
|
||||
/// BR_NOOP,
|
||||
/// BR_SPAWN_LOOPER,
|
||||
/// BR_DEAD_BINDER,
|
||||
/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
|
||||
/// BR_FAILED_REPLY
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
|
||||
/// ```
|
||||
///
|
||||
/// # Modifiers
|
||||
///
|
||||
/// For each identifier, it is possible to attach one or multiple modifiers to
|
||||
/// it.
|
||||
///
|
||||
/// Currently supported modifiers are:
|
||||
/// * `span`: change the span of concatenated identifier to the span of the specified token. By
|
||||
/// default the span of the `[< >]` group is used.
|
||||
/// * `lower`: change the identifier to lower case.
|
||||
/// * `upper`: change the identifier to upper case.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use kernel::macro::paste;
|
||||
///
|
||||
/// macro_rules! pub_no_prefix {
|
||||
/// ($prefix:ident, $($newname:ident),+) => {
|
||||
/// kernel::macros::paste! {
|
||||
/// $(pub(crate) const fn [<$newname:lower:span>]: u32 = [<$prefix $newname:span>];)+
|
||||
/// }
|
||||
/// };
|
||||
/// }
|
||||
///
|
||||
/// pub_no_prefix!(
|
||||
/// binder_driver_return_protocol_,
|
||||
/// BR_OK,
|
||||
/// BR_ERROR,
|
||||
/// BR_TRANSACTION,
|
||||
/// BR_REPLY,
|
||||
/// BR_DEAD_REPLY,
|
||||
/// BR_TRANSACTION_COMPLETE,
|
||||
/// BR_INCREFS,
|
||||
/// BR_ACQUIRE,
|
||||
/// BR_RELEASE,
|
||||
/// BR_DECREFS,
|
||||
/// BR_NOOP,
|
||||
/// BR_SPAWN_LOOPER,
|
||||
/// BR_DEAD_BINDER,
|
||||
/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
|
||||
/// BR_FAILED_REPLY
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(br_ok(), binder_driver_return_protocol_BR_OK);
|
||||
/// ```
|
||||
///
|
||||
/// [`paste`]: https://docs.rs/paste/
|
||||
#[proc_macro]
|
||||
pub fn paste(input: TokenStream) -> TokenStream {
|
||||
let mut tokens = input.into_iter().collect();
|
||||
paste::expand(&mut tokens);
|
||||
tokens.into_iter().collect()
|
||||
}
|
||||
|
||||
/// Derives the [`Zeroable`] trait for the given struct.
|
||||
///
|
||||
/// This can only be used for structs where every field implements the [`Zeroable`] trait.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Zeroable)]
|
||||
/// pub struct DriverData {
|
||||
/// id: i64,
|
||||
/// buf_ptr: *mut u8,
|
||||
/// len: usize,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(Zeroable)]
|
||||
pub fn derive_zeroable(input: TokenStream) -> TokenStream {
|
||||
zeroable::derive(input)
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
||||
/// Used by the printing macros, e.g. [`info!`].
|
||||
const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
|
||||
|
||||
/// The \"Rust loadable module\" mark, for `scripts/is_rust_module.sh`.
|
||||
/// The \"Rust loadable module\" mark.
|
||||
//
|
||||
// This may be best done another way later on, e.g. as a new modinfo
|
||||
// key or a new section. For the moment, keep it simple.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
|
||||
|
||||
fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
|
||||
let mut tokens = tokens.iter();
|
||||
let mut segments = Vec::new();
|
||||
let mut span = None;
|
||||
loop {
|
||||
match tokens.next() {
|
||||
None => break,
|
||||
Some(TokenTree::Literal(lit)) => segments.push((lit.to_string(), lit.span())),
|
||||
Some(TokenTree::Ident(ident)) => {
|
||||
let mut value = ident.to_string();
|
||||
if value.starts_with("r#") {
|
||||
value.replace_range(0..2, "");
|
||||
}
|
||||
segments.push((value, ident.span()));
|
||||
}
|
||||
Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
|
||||
let Some(TokenTree::Ident(ident)) = tokens.next() else {
|
||||
panic!("expected identifier as modifier");
|
||||
};
|
||||
|
||||
let (mut value, sp) = segments.pop().expect("expected identifier before modifier");
|
||||
match ident.to_string().as_str() {
|
||||
// Set the overall span of concatenated token as current span
|
||||
"span" => {
|
||||
assert!(
|
||||
span.is_none(),
|
||||
"span modifier should only appear at most once"
|
||||
);
|
||||
span = Some(sp);
|
||||
}
|
||||
"lower" => value = value.to_lowercase(),
|
||||
"upper" => value = value.to_uppercase(),
|
||||
v => panic!("unknown modifier `{v}`"),
|
||||
};
|
||||
segments.push((value, sp));
|
||||
}
|
||||
_ => panic!("unexpected token in paste segments"),
|
||||
};
|
||||
}
|
||||
|
||||
let pasted: String = segments.into_iter().map(|x| x.0).collect();
|
||||
TokenTree::Ident(Ident::new(&pasted, span.unwrap_or(group_span)))
|
||||
}
|
||||
|
||||
pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
|
||||
for token in tokens.iter_mut() {
|
||||
if let TokenTree::Group(group) = token {
|
||||
let delimiter = group.delimiter();
|
||||
let span = group.span();
|
||||
let mut stream: Vec<_> = group.stream().into_iter().collect();
|
||||
// Find groups that looks like `[< A B C D >]`
|
||||
if delimiter == Delimiter::Bracket
|
||||
&& stream.len() >= 3
|
||||
&& matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')
|
||||
&& matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')
|
||||
{
|
||||
// Replace the group with concatenated token
|
||||
*token = concat(&stream[1..stream.len() - 1], span);
|
||||
} else {
|
||||
// Recursively expand tokens inside the group
|
||||
expand(&mut stream);
|
||||
let mut group = Group::new(delimiter, stream.into_iter().collect());
|
||||
group.set_span(span);
|
||||
*token = TokenTree::Group(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Path segments cannot contain invisible delimiter group, so remove them if any.
|
||||
for i in (0..tokens.len().saturating_sub(3)).rev() {
|
||||
// Looking for a double colon
|
||||
if matches!(
|
||||
(&tokens[i + 1], &tokens[i + 2]),
|
||||
(TokenTree::Punct(a), TokenTree::Punct(b))
|
||||
if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'
|
||||
) {
|
||||
match &tokens[i + 3] {
|
||||
TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
|
||||
tokens.splice(i + 3..i + 4, group.stream());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match &tokens[i] {
|
||||
TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
|
||||
tokens.splice(i..i + 1, group.stream());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,18 @@ macro_rules! quote_spanned {
|
||||
));
|
||||
quote_spanned!(@proc $v $span $($tt)*);
|
||||
};
|
||||
(@proc $v:ident $span:ident ; $($tt:tt)*) => {
|
||||
$v.push(::proc_macro::TokenTree::Punct(
|
||||
::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone)
|
||||
));
|
||||
quote_spanned!(@proc $v $span $($tt)*);
|
||||
};
|
||||
(@proc $v:ident $span:ident + $($tt:tt)*) => {
|
||||
$v.push(::proc_macro::TokenTree::Punct(
|
||||
::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone)
|
||||
));
|
||||
quote_spanned!(@proc $v $span $($tt)*);
|
||||
};
|
||||
(@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
|
||||
$v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span)));
|
||||
quote_spanned!(@proc $v $span $($tt)*);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use crate::helpers::{parse_generics, Generics};
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
|
||||
pub(crate) fn derive(input: TokenStream) -> TokenStream {
|
||||
let (
|
||||
Generics {
|
||||
impl_generics,
|
||||
ty_generics,
|
||||
},
|
||||
mut rest,
|
||||
) = parse_generics(input);
|
||||
// This should be the body of the struct `{...}`.
|
||||
let last = rest.pop();
|
||||
// Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`.
|
||||
let mut new_impl_generics = Vec::with_capacity(impl_generics.len());
|
||||
// Are we inside of a generic where we want to add `Zeroable`?
|
||||
let mut in_generic = !impl_generics.is_empty();
|
||||
// Have we already inserted `Zeroable`?
|
||||
let mut inserted = false;
|
||||
// Level of `<>` nestings.
|
||||
let mut nested = 0;
|
||||
for tt in impl_generics {
|
||||
match &tt {
|
||||
// If we find a `,`, then we have finished a generic/constant/lifetime parameter.
|
||||
TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => {
|
||||
if in_generic && !inserted {
|
||||
new_impl_generics.extend(quote! { : ::kernel::init::Zeroable });
|
||||
}
|
||||
in_generic = true;
|
||||
inserted = false;
|
||||
new_impl_generics.push(tt);
|
||||
}
|
||||
// If we find `'`, then we are entering a lifetime.
|
||||
TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => {
|
||||
in_generic = false;
|
||||
new_impl_generics.push(tt);
|
||||
}
|
||||
TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => {
|
||||
new_impl_generics.push(tt);
|
||||
if in_generic {
|
||||
new_impl_generics.extend(quote! { ::kernel::init::Zeroable + });
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
TokenTree::Punct(p) if p.as_char() == '<' => {
|
||||
nested += 1;
|
||||
new_impl_generics.push(tt);
|
||||
}
|
||||
TokenTree::Punct(p) if p.as_char() == '>' => {
|
||||
assert!(nested > 0);
|
||||
nested -= 1;
|
||||
new_impl_generics.push(tt);
|
||||
}
|
||||
_ => new_impl_generics.push(tt),
|
||||
}
|
||||
}
|
||||
assert_eq!(nested, 0);
|
||||
if in_generic && !inserted {
|
||||
new_impl_generics.extend(quote! { : ::kernel::init::Zeroable });
|
||||
}
|
||||
quote! {
|
||||
::kernel::__derive_zeroable!(
|
||||
parse_input:
|
||||
@sig(#(#rest)*),
|
||||
@impl_generics(#(#new_impl_generics)*),
|
||||
@ty_generics(#(#ty_generics)*),
|
||||
@body(#last),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,6 @@ quiet_cmd_btf_ko = BTF [M] $@
|
||||
cmd_btf_ko = \
|
||||
if [ ! -f $(mixed-build-prefix)vmlinux ]; then \
|
||||
printf "Skipping BTF generation for %s due to unavailability of $(mixed-build-prefix)vmlinux\n" $@ 1>&2; \
|
||||
elif [ -n "$(CONFIG_RUST)" ] && $(srctree)/scripts/is_rust_module.sh $@; then \
|
||||
printf "Skipping BTF generation for %s because it's a Rust module\n" $@ 1>&2; \
|
||||
else \
|
||||
LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) --btf_base $(mixed-build-prefix)vmlinux $@; \
|
||||
$(RESOLVE_BTFIDS) -b $(mixed-build-prefix)vmlinux $@; \
|
||||
|
||||
@@ -6,10 +6,19 @@
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
def generate_crates(srctree, objtree, sysroot_src):
|
||||
def args_crates_cfgs(cfgs):
|
||||
crates_cfgs = {}
|
||||
for cfg in cfgs:
|
||||
crate, vals = cfg.split("=", 1)
|
||||
crates_cfgs[crate] = vals.replace("--cfg", "").split()
|
||||
|
||||
return crates_cfgs
|
||||
|
||||
def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
|
||||
# Generate the configuration list.
|
||||
cfg = []
|
||||
with open(objtree / "include" / "generated" / "rustc_cfg") as fd:
|
||||
@@ -23,6 +32,7 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
# Avoid O(n^2) iterations by keeping a map of indexes.
|
||||
crates = []
|
||||
crates_indexes = {}
|
||||
crates_cfgs = args_crates_cfgs(cfgs)
|
||||
|
||||
def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False):
|
||||
crates_indexes[display_name] = len(crates)
|
||||
@@ -44,6 +54,7 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
"core",
|
||||
sysroot_src / "core" / "src" / "lib.rs",
|
||||
[],
|
||||
cfg=crates_cfgs.get("core", []),
|
||||
is_workspace_member=False,
|
||||
)
|
||||
|
||||
@@ -57,6 +68,7 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
"alloc",
|
||||
srctree / "rust" / "alloc" / "lib.rs",
|
||||
["core", "compiler_builtins"],
|
||||
cfg=crates_cfgs.get("alloc", []),
|
||||
)
|
||||
|
||||
append_crate(
|
||||
@@ -65,7 +77,7 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
[],
|
||||
is_proc_macro=True,
|
||||
)
|
||||
crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so"
|
||||
crates[-1]["proc_macro_dylib_path"] = f"{objtree}/rust/libmacros.so"
|
||||
|
||||
append_crate(
|
||||
"build_error",
|
||||
@@ -95,19 +107,26 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
"exclude_dirs": [],
|
||||
}
|
||||
|
||||
def is_root_crate(build_file, target):
|
||||
try:
|
||||
return f"{target}.o" in open(build_file).read()
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
# Then, the rest outside of `rust/`.
|
||||
#
|
||||
# We explicitly mention the top-level folders we want to cover.
|
||||
for folder in ("samples", "drivers"):
|
||||
for path in (srctree / folder).rglob("*.rs"):
|
||||
extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers"))
|
||||
if external_src is not None:
|
||||
extra_dirs = [external_src]
|
||||
for folder in extra_dirs:
|
||||
for path in folder.rglob("*.rs"):
|
||||
logging.info("Checking %s", path)
|
||||
name = path.name.replace(".rs", "")
|
||||
|
||||
# Skip those that are not crate roots.
|
||||
try:
|
||||
if f"{name}.o" not in open(path.parent / "Makefile").read():
|
||||
continue
|
||||
except FileNotFoundError:
|
||||
if not is_root_crate(path.parent / "Makefile", name) and \
|
||||
not is_root_crate(path.parent / "Kbuild", name):
|
||||
continue
|
||||
|
||||
logging.info("Adding %s", name)
|
||||
@@ -123,9 +142,11 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--verbose', '-v', action='store_true')
|
||||
parser.add_argument('--cfgs', action='append', default=[])
|
||||
parser.add_argument("srctree", type=pathlib.Path)
|
||||
parser.add_argument("objtree", type=pathlib.Path)
|
||||
parser.add_argument("sysroot_src", type=pathlib.Path)
|
||||
parser.add_argument("exttree", type=pathlib.Path, nargs="?")
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(
|
||||
@@ -134,7 +155,7 @@ def main():
|
||||
)
|
||||
|
||||
rust_project = {
|
||||
"crates": generate_crates(args.srctree, args.objtree, args.sysroot_src),
|
||||
"crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs),
|
||||
"sysroot_src": str(args.sysroot_src),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# is_rust_module.sh module.ko
|
||||
#
|
||||
# Returns `0` if `module.ko` is a Rust module, `1` otherwise.
|
||||
|
||||
set -e
|
||||
|
||||
# Using the `16_` prefix ensures other symbols with the same substring
|
||||
# are not picked up (even if it would be unlikely). The last part is
|
||||
# used just in case LLVM decides to use the `.` suffix.
|
||||
#
|
||||
# In the future, checking for the `.comment` section may be another
|
||||
# option, see https://github.com/rust-lang/rust/pull/97550.
|
||||
${NM} "$*" | grep -qE '^[0-9a-fA-F]+ [Rr] _R[^[:space:]]+16___IS_RUST_MODULE[^[:space:]]*$'
|
||||
@@ -31,10 +31,10 @@ llvm)
|
||||
fi
|
||||
;;
|
||||
rustc)
|
||||
echo 1.68.2
|
||||
echo 1.71.1
|
||||
;;
|
||||
bindgen)
|
||||
echo 0.56.0
|
||||
echo 0.65.1
|
||||
;;
|
||||
*)
|
||||
echo "$1: unknown tool" >&2
|
||||
|
||||
+167
-66
@@ -2,8 +2,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Tests whether a suitable Rust toolchain is available.
|
||||
#
|
||||
# Pass `-v` for human output and more checks (as warnings).
|
||||
|
||||
set -e
|
||||
|
||||
@@ -21,102 +19,208 @@ get_canonical_version()
|
||||
echo $((100000 * $1 + 100 * $2 + $3))
|
||||
}
|
||||
|
||||
# Print a reference to the Quick Start guide in the documentation.
|
||||
print_docs_reference()
|
||||
{
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Please see Documentation/rust/quick-start.rst for details"
|
||||
echo >&2 "*** on how to set up the Rust support."
|
||||
echo >&2 "***"
|
||||
}
|
||||
|
||||
# Print an explanation about the fact that the script is meant to be called from Kbuild.
|
||||
print_kbuild_explanation()
|
||||
{
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** This script is intended to be called from Kbuild."
|
||||
echo >&2 "*** Please use the 'rustavailable' target to call it instead."
|
||||
echo >&2 "*** Otherwise, the results may not be meaningful."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If the script fails for any reason, or if there was any warning, then
|
||||
# print a reference to the documentation on exit.
|
||||
warning=0
|
||||
trap 'if [ $? -ne 0 ] || [ $warning -ne 0 ]; then print_docs_reference; fi' EXIT
|
||||
|
||||
# Check that the expected environment variables are set.
|
||||
if [ -z "${RUSTC+x}" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Environment variable 'RUSTC' is not set."
|
||||
print_kbuild_explanation
|
||||
fi
|
||||
|
||||
if [ -z "${BINDGEN+x}" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Environment variable 'BINDGEN' is not set."
|
||||
print_kbuild_explanation
|
||||
fi
|
||||
|
||||
if [ -z "${CC+x}" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Environment variable 'CC' is not set."
|
||||
print_kbuild_explanation
|
||||
fi
|
||||
|
||||
# Check that the Rust compiler exists.
|
||||
if ! command -v "$RUSTC" >/dev/null; then
|
||||
if [ "$1" = -v ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust compiler '$RUSTC' could not be found."
|
||||
echo >&2 "***"
|
||||
fi
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust compiler '$RUSTC' could not be found."
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that the Rust bindings generator exists.
|
||||
if ! command -v "$BINDGEN" >/dev/null; then
|
||||
if [ "$1" = -v ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust bindings generator '$BINDGEN' could not be found."
|
||||
echo >&2 "***"
|
||||
fi
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust bindings generator '$BINDGEN' could not be found."
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that the Rust compiler version is suitable.
|
||||
#
|
||||
# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
|
||||
rust_compiler_output=$( \
|
||||
LC_ALL=C "$RUSTC" --version 2>/dev/null
|
||||
) || rust_compiler_code=$?
|
||||
if [ -n "$rust_compiler_code" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Running '$RUSTC' to check the Rust compiler version failed with"
|
||||
echo >&2 "*** code $rust_compiler_code. See output and docs below for details:"
|
||||
echo >&2 "***"
|
||||
echo >&2 "$rust_compiler_output"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
rust_compiler_version=$( \
|
||||
LC_ALL=C "$RUSTC" --version 2>/dev/null \
|
||||
| head -n 1 \
|
||||
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' \
|
||||
echo "$rust_compiler_output" \
|
||||
| sed -nE '1s:.*rustc ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
|
||||
)
|
||||
if [ -z "$rust_compiler_version" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Running '$RUSTC' to check the Rust compiler version did not return"
|
||||
echo >&2 "*** an expected output. See output and docs below for details:"
|
||||
echo >&2 "***"
|
||||
echo >&2 "$rust_compiler_output"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
rust_compiler_min_version=$($min_tool_version rustc)
|
||||
rust_compiler_cversion=$(get_canonical_version $rust_compiler_version)
|
||||
rust_compiler_min_cversion=$(get_canonical_version $rust_compiler_min_version)
|
||||
if [ "$rust_compiler_cversion" -lt "$rust_compiler_min_cversion" ]; then
|
||||
if [ "$1" = -v ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust compiler '$RUSTC' is too old."
|
||||
echo >&2 "*** Your version: $rust_compiler_version"
|
||||
echo >&2 "*** Minimum version: $rust_compiler_min_version"
|
||||
echo >&2 "***"
|
||||
fi
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust compiler '$RUSTC' is too old."
|
||||
echo >&2 "*** Your version: $rust_compiler_version"
|
||||
echo >&2 "*** Minimum version: $rust_compiler_min_version"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$1" = -v ] && [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then
|
||||
if [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust compiler '$RUSTC' is too new. This may or may not work."
|
||||
echo >&2 "*** Your version: $rust_compiler_version"
|
||||
echo >&2 "*** Expected version: $rust_compiler_min_version"
|
||||
echo >&2 "***"
|
||||
warning=1
|
||||
fi
|
||||
|
||||
# Check that the Rust bindings generator is suitable.
|
||||
#
|
||||
# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
|
||||
rust_bindings_generator_output=$( \
|
||||
LC_ALL=C "$BINDGEN" --version 2>/dev/null
|
||||
) || rust_bindings_generator_code=$?
|
||||
if [ -n "$rust_bindings_generator_code" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Running '$BINDGEN' to check the Rust bindings generator version failed with"
|
||||
echo >&2 "*** code $rust_bindings_generator_code. See output and docs below for details:"
|
||||
echo >&2 "***"
|
||||
echo >&2 "$rust_bindings_generator_output"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
rust_bindings_generator_version=$( \
|
||||
LC_ALL=C "$BINDGEN" --version 2>/dev/null \
|
||||
| head -n 1 \
|
||||
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' \
|
||||
echo "$rust_bindings_generator_output" \
|
||||
| sed -nE '1s:.*bindgen ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
|
||||
)
|
||||
if [ -z "$rust_bindings_generator_version" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Running '$BINDGEN' to check the bindings generator version did not return"
|
||||
echo >&2 "*** an expected output. See output and docs below for details:"
|
||||
echo >&2 "***"
|
||||
echo >&2 "$rust_bindings_generator_output"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
rust_bindings_generator_min_version=$($min_tool_version bindgen)
|
||||
rust_bindings_generator_cversion=$(get_canonical_version $rust_bindings_generator_version)
|
||||
rust_bindings_generator_min_cversion=$(get_canonical_version $rust_bindings_generator_min_version)
|
||||
if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cversion" ]; then
|
||||
if [ "$1" = -v ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust bindings generator '$BINDGEN' is too old."
|
||||
echo >&2 "*** Your version: $rust_bindings_generator_version"
|
||||
echo >&2 "*** Minimum version: $rust_bindings_generator_min_version"
|
||||
echo >&2 "***"
|
||||
fi
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust bindings generator '$BINDGEN' is too old."
|
||||
echo >&2 "*** Your version: $rust_bindings_generator_version"
|
||||
echo >&2 "*** Minimum version: $rust_bindings_generator_min_version"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$1" = -v ] && [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then
|
||||
if [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Rust bindings generator '$BINDGEN' is too new. This may or may not work."
|
||||
echo >&2 "*** Your version: $rust_bindings_generator_version"
|
||||
echo >&2 "*** Expected version: $rust_bindings_generator_min_version"
|
||||
echo >&2 "***"
|
||||
warning=1
|
||||
fi
|
||||
|
||||
# Check that the `libclang` used by the Rust bindings generator is suitable.
|
||||
#
|
||||
# In order to do that, first invoke `bindgen` to get the `libclang` version
|
||||
# found by `bindgen`. This step may already fail if, for instance, `libclang`
|
||||
# is not found, thus inform the user in such a case.
|
||||
bindgen_libclang_output=$( \
|
||||
LC_ALL=C "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang.h 2>&1 >/dev/null
|
||||
) || bindgen_libclang_code=$?
|
||||
if [ -n "$bindgen_libclang_code" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Running '$BINDGEN' to check the libclang version (used by the Rust"
|
||||
echo >&2 "*** bindings generator) failed with code $bindgen_libclang_code. This may be caused by"
|
||||
echo >&2 "*** a failure to locate libclang. See output and docs below for details:"
|
||||
echo >&2 "***"
|
||||
echo >&2 "$bindgen_libclang_output"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# `bindgen` returned successfully, thus use the output to check that the version
|
||||
# of the `libclang` found by the Rust bindings generator is suitable.
|
||||
#
|
||||
# Unlike other version checks, note that this one does not necessarily appear
|
||||
# in the first line of the output, thus no `sed` address is provided.
|
||||
bindgen_libclang_version=$( \
|
||||
LC_ALL=C "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang.h 2>&1 >/dev/null \
|
||||
| grep -F 'clang version ' \
|
||||
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' \
|
||||
| head -n 1 \
|
||||
echo "$bindgen_libclang_output" \
|
||||
| sed -nE 's:.*clang version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
|
||||
)
|
||||
if [ -z "$bindgen_libclang_version" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Running '$BINDGEN' to check the libclang version (used by the Rust"
|
||||
echo >&2 "*** bindings generator) did not return an expected output. See output"
|
||||
echo >&2 "*** and docs below for details:"
|
||||
echo >&2 "***"
|
||||
echo >&2 "$bindgen_libclang_output"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
bindgen_libclang_min_version=$($min_tool_version llvm)
|
||||
bindgen_libclang_cversion=$(get_canonical_version $bindgen_libclang_version)
|
||||
bindgen_libclang_min_cversion=$(get_canonical_version $bindgen_libclang_min_version)
|
||||
if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
|
||||
if [ "$1" = -v ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') is too old."
|
||||
echo >&2 "*** Your version: $bindgen_libclang_version"
|
||||
echo >&2 "*** Minimum version: $bindgen_libclang_min_version"
|
||||
echo >&2 "***"
|
||||
fi
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') is too old."
|
||||
echo >&2 "*** Your version: $bindgen_libclang_version"
|
||||
echo >&2 "*** Minimum version: $bindgen_libclang_min_version"
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -125,21 +229,20 @@ fi
|
||||
#
|
||||
# In the future, we might be able to perform a full version check, see
|
||||
# https://github.com/rust-lang/rust-bindgen/issues/2138.
|
||||
if [ "$1" = -v ]; then
|
||||
cc_name=$($(dirname $0)/cc-version.sh "$CC" | cut -f1 -d' ')
|
||||
if [ "$cc_name" = Clang ]; then
|
||||
clang_version=$( \
|
||||
LC_ALL=C "$CC" --version 2>/dev/null \
|
||||
| sed -nE '1s:.*version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
|
||||
)
|
||||
if [ "$clang_version" != "$bindgen_libclang_version" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN')"
|
||||
echo >&2 "*** version does not match Clang's. This may be a problem."
|
||||
echo >&2 "*** libclang version: $bindgen_libclang_version"
|
||||
echo >&2 "*** Clang version: $clang_version"
|
||||
echo >&2 "***"
|
||||
fi
|
||||
cc_name=$($(dirname $0)/cc-version.sh $CC | cut -f1 -d' ')
|
||||
if [ "$cc_name" = Clang ]; then
|
||||
clang_version=$( \
|
||||
LC_ALL=C $CC --version 2>/dev/null \
|
||||
| sed -nE '1s:.*version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
|
||||
)
|
||||
if [ "$clang_version" != "$bindgen_libclang_version" ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN')"
|
||||
echo >&2 "*** version does not match Clang's. This may be a problem."
|
||||
echo >&2 "*** libclang version: $bindgen_libclang_version"
|
||||
echo >&2 "*** Clang version: $clang_version"
|
||||
echo >&2 "***"
|
||||
warning=1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -150,11 +253,9 @@ rustc_sysroot=$("$RUSTC" $KRUSTFLAGS --print sysroot)
|
||||
rustc_src=${RUST_LIB_SRC:-"$rustc_sysroot/lib/rustlib/src/rust/library"}
|
||||
rustc_src_core="$rustc_src/core/src/lib.rs"
|
||||
if [ ! -e "$rustc_src_core" ]; then
|
||||
if [ "$1" = -v ]; then
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Source code for the 'core' standard library could not be found"
|
||||
echo >&2 "*** at '$rustc_src_core'."
|
||||
echo >&2 "***"
|
||||
fi
|
||||
echo >&2 "***"
|
||||
echo >&2 "*** Source code for the 'core' standard library could not be found"
|
||||
echo >&2 "*** at '$rustc_src_core'."
|
||||
echo >&2 "***"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Executable
+346
@@ -0,0 +1,346 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
"""Tests the `rust_is_available.sh` script.
|
||||
|
||||
Some of the tests require the real programs to be available in `$PATH`
|
||||
under their canonical name (and with the expected versions).
|
||||
"""
|
||||
|
||||
import enum
|
||||
import os
|
||||
import pathlib
|
||||
import stat
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
class TestRustIsAvailable(unittest.TestCase):
|
||||
@enum.unique
|
||||
class Expected(enum.Enum):
|
||||
SUCCESS = enum.auto()
|
||||
SUCCESS_WITH_WARNINGS = enum.auto()
|
||||
SUCCESS_WITH_EXTRA_OUTPUT = enum.auto()
|
||||
FAILURE = enum.auto()
|
||||
|
||||
@classmethod
|
||||
def generate_executable(cls, content):
|
||||
path = pathlib.Path(cls.tempdir.name)
|
||||
name = str(len(tuple(path.iterdir())))
|
||||
path = path / name
|
||||
with open(path, "w") as file_:
|
||||
file_.write(content)
|
||||
os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
|
||||
return path
|
||||
|
||||
@classmethod
|
||||
def generate_clang(cls, stdout):
|
||||
return cls.generate_executable(f"""#!/usr/bin/env python3
|
||||
import sys
|
||||
if "-E" in " ".join(sys.argv):
|
||||
print({repr("Clang " + " ".join(cls.llvm_default_version.split(" ")))})
|
||||
else:
|
||||
print({repr(stdout)})
|
||||
""")
|
||||
|
||||
@classmethod
|
||||
def generate_rustc(cls, stdout):
|
||||
return cls.generate_executable(f"""#!/usr/bin/env python3
|
||||
import sys
|
||||
if "--print sysroot" in " ".join(sys.argv):
|
||||
print({repr(cls.rust_default_sysroot)})
|
||||
else:
|
||||
print({repr(stdout)})
|
||||
""")
|
||||
|
||||
@classmethod
|
||||
def generate_bindgen(cls, version_stdout, libclang_stderr):
|
||||
return cls.generate_executable(f"""#!/usr/bin/env python3
|
||||
import sys
|
||||
if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
|
||||
print({repr(libclang_stderr)}, file=sys.stderr)
|
||||
else:
|
||||
print({repr(version_stdout)})
|
||||
""")
|
||||
|
||||
@classmethod
|
||||
def generate_bindgen_version(cls, stdout):
|
||||
return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr)
|
||||
|
||||
@classmethod
|
||||
def generate_bindgen_libclang(cls, stderr):
|
||||
return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, stderr)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.tempdir = tempfile.TemporaryDirectory()
|
||||
|
||||
cls.missing = pathlib.Path(cls.tempdir.name) / "missing"
|
||||
|
||||
cls.nonexecutable = pathlib.Path(cls.tempdir.name) / "nonexecutable"
|
||||
with open(cls.nonexecutable, "w") as file_:
|
||||
file_.write("nonexecutable")
|
||||
|
||||
cls.unexpected_binary = "true"
|
||||
|
||||
cls.rustc_default_version = subprocess.check_output(("scripts/min-tool-version.sh", "rustc")).decode().strip()
|
||||
cls.bindgen_default_version = subprocess.check_output(("scripts/min-tool-version.sh", "bindgen")).decode().strip()
|
||||
cls.llvm_default_version = subprocess.check_output(("scripts/min-tool-version.sh", "llvm")).decode().strip()
|
||||
cls.rust_default_sysroot = subprocess.check_output(("rustc", "--print", "sysroot")).decode().strip()
|
||||
|
||||
cls.bindgen_default_bindgen_version_stdout = f"bindgen {cls.bindgen_default_version}"
|
||||
cls.bindgen_default_bindgen_libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {cls.llvm_default_version} [-W#pragma-messages], err: false"
|
||||
|
||||
cls.default_rustc = cls.generate_rustc(f"rustc {cls.rustc_default_version}")
|
||||
cls.default_bindgen = cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, cls.bindgen_default_bindgen_libclang_stderr)
|
||||
cls.default_cc = cls.generate_clang(f"clang version {cls.llvm_default_version}")
|
||||
|
||||
def run_script(self, expected, override_env):
|
||||
env = {
|
||||
"RUSTC": self.default_rustc,
|
||||
"BINDGEN": self.default_bindgen,
|
||||
"CC": self.default_cc,
|
||||
}
|
||||
|
||||
for key, value in override_env.items():
|
||||
if value is None:
|
||||
del env[key]
|
||||
continue
|
||||
env[key] = value
|
||||
|
||||
result = subprocess.run("scripts/rust_is_available.sh", env=env, capture_output=True)
|
||||
|
||||
# The script should never output anything to `stdout`.
|
||||
self.assertEqual(result.stdout, b"")
|
||||
|
||||
if expected == self.Expected.SUCCESS:
|
||||
# When expecting a success, the script should return 0
|
||||
# and it should not output anything to `stderr`.
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertEqual(result.stderr, b"")
|
||||
elif expected == self.Expected.SUCCESS_WITH_EXTRA_OUTPUT:
|
||||
# When expecting a success with extra output (that is not warnings,
|
||||
# which is the common case), the script should return 0 and it
|
||||
# should output at least something to `stderr` (the output should
|
||||
# be checked further by the test).
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertNotEqual(result.stderr, b"")
|
||||
elif expected == self.Expected.SUCCESS_WITH_WARNINGS:
|
||||
# When expecting a success with warnings, the script should return 0
|
||||
# and it should output at least the instructions to `stderr`.
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertIn(b"Please see Documentation/rust/quick-start.rst for details", result.stderr)
|
||||
else:
|
||||
# When expecting a failure, the script should return non-0
|
||||
# and it should output at least the instructions to `stderr`.
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn(b"Please see Documentation/rust/quick-start.rst for details", result.stderr)
|
||||
|
||||
# The output will generally be UTF-8 (i.e. unless the user has
|
||||
# put strange values in the environment).
|
||||
result.stderr = result.stderr.decode()
|
||||
|
||||
return result
|
||||
|
||||
def test_rustc_unset(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": None })
|
||||
self.assertIn("Environment variable 'RUSTC' is not set.", result.stderr)
|
||||
self.assertIn("This script is intended to be called from Kbuild.", result.stderr)
|
||||
|
||||
def test_bindgen_unset(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": None })
|
||||
self.assertIn("Environment variable 'BINDGEN' is not set.", result.stderr)
|
||||
self.assertIn("This script is intended to be called from Kbuild.", result.stderr)
|
||||
|
||||
def test_cc_unset(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "CC": None })
|
||||
self.assertIn("Environment variable 'CC' is not set.", result.stderr)
|
||||
self.assertIn("This script is intended to be called from Kbuild.", result.stderr)
|
||||
|
||||
def test_rustc_missing(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.missing })
|
||||
self.assertIn(f"Rust compiler '{self.missing}' could not be found.", result.stderr)
|
||||
|
||||
def test_bindgen_missing(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.missing })
|
||||
self.assertIn(f"Rust bindings generator '{self.missing}' could not be found.", result.stderr)
|
||||
|
||||
def test_rustc_nonexecutable(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.nonexecutable })
|
||||
self.assertIn(f"Running '{self.nonexecutable}' to check the Rust compiler version failed with", result.stderr)
|
||||
|
||||
def test_rustc_unexpected_binary(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.unexpected_binary })
|
||||
self.assertIn(f"Running '{self.unexpected_binary}' to check the Rust compiler version did not return", result.stderr)
|
||||
|
||||
def test_rustc_unexpected_name(self):
|
||||
rustc = self.generate_rustc(f"unexpected {self.rustc_default_version} (a8314ef7d 2022-06-27)")
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
|
||||
self.assertIn(f"Running '{rustc}' to check the Rust compiler version did not return", result.stderr)
|
||||
|
||||
def test_rustc_unexpected_version(self):
|
||||
rustc = self.generate_rustc("rustc unexpected (a8314ef7d 2022-06-27)")
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
|
||||
self.assertIn(f"Running '{rustc}' to check the Rust compiler version did not return", result.stderr)
|
||||
|
||||
def test_rustc_no_minor(self):
|
||||
rustc = self.generate_rustc(f"rustc {'.'.join(self.rustc_default_version.split('.')[:2])} (a8314ef7d 2022-06-27)")
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
|
||||
self.assertIn(f"Running '{rustc}' to check the Rust compiler version did not return", result.stderr)
|
||||
|
||||
def test_rustc_old_version(self):
|
||||
rustc = self.generate_rustc("rustc 1.60.0 (a8314ef7d 2022-06-27)")
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
|
||||
self.assertIn(f"Rust compiler '{rustc}' is too old.", result.stderr)
|
||||
|
||||
def test_rustc_new_version(self):
|
||||
rustc = self.generate_rustc("rustc 1.999.0 (a8314ef7d 2099-06-27)")
|
||||
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "RUSTC": rustc })
|
||||
self.assertIn(f"Rust compiler '{rustc}' is too new. This may or may not work.", result.stderr)
|
||||
|
||||
def test_bindgen_nonexecutable(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.nonexecutable })
|
||||
self.assertIn(f"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with", result.stderr)
|
||||
|
||||
def test_bindgen_unexpected_binary(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.unexpected_binary })
|
||||
self.assertIn(f"Running '{self.unexpected_binary}' to check the bindings generator version did not return", result.stderr)
|
||||
|
||||
def test_bindgen_unexpected_name(self):
|
||||
bindgen = self.generate_bindgen_version(f"unexpected {self.bindgen_default_version}")
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"Running '{bindgen}' to check the bindings generator version did not return", result.stderr)
|
||||
|
||||
def test_bindgen_unexpected_version(self):
|
||||
bindgen = self.generate_bindgen_version("bindgen unexpected")
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"Running '{bindgen}' to check the bindings generator version did not return", result.stderr)
|
||||
|
||||
def test_bindgen_no_minor(self):
|
||||
bindgen = self.generate_bindgen_version(f"bindgen {'.'.join(self.bindgen_default_version.split('.')[:2])}")
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"Running '{bindgen}' to check the bindings generator version did not return", result.stderr)
|
||||
|
||||
def test_bindgen_old_version(self):
|
||||
bindgen = self.generate_bindgen_version("bindgen 0.50.0")
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr)
|
||||
|
||||
def test_bindgen_new_version(self):
|
||||
bindgen = self.generate_bindgen_version("bindgen 0.999.0")
|
||||
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"Rust bindings generator '{bindgen}' is too new. This may or may not work.", result.stderr)
|
||||
|
||||
def test_bindgen_libclang_failure(self):
|
||||
for env in (
|
||||
{ "LLVM_CONFIG_PATH": self.missing },
|
||||
{ "LIBCLANG_PATH": self.missing },
|
||||
{ "CLANG_PATH": self.missing },
|
||||
):
|
||||
with self.subTest(env=env):
|
||||
result = self.run_script(self.Expected.FAILURE, env | { "PATH": os.environ["PATH"], "BINDGEN": "bindgen" })
|
||||
self.assertIn("Running 'bindgen' to check the libclang version (used by the Rust", result.stderr)
|
||||
self.assertIn("bindings generator) failed with code ", result.stderr)
|
||||
|
||||
def test_bindgen_libclang_unexpected_version(self):
|
||||
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version unexpected [-W#pragma-messages], err: false")
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"Running '{bindgen}' to check the libclang version (used by the Rust", result.stderr)
|
||||
self.assertIn("bindings generator) did not return an expected output. See output", result.stderr)
|
||||
|
||||
def test_bindgen_libclang_old_version(self):
|
||||
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 10.0.0 [-W#pragma-messages], err: false")
|
||||
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
|
||||
self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
|
||||
|
||||
def test_clang_matches_bindgen_libclang_different_bindgen(self):
|
||||
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
|
||||
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
|
||||
self.assertIn("version does not match Clang's. This may be a problem.", result.stderr)
|
||||
|
||||
def test_clang_matches_bindgen_libclang_different_clang(self):
|
||||
cc = self.generate_clang("clang version 999.0.0")
|
||||
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "CC": cc })
|
||||
self.assertIn("version does not match Clang's. This may be a problem.", result.stderr)
|
||||
|
||||
def test_rustc_src_core_krustflags(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "PATH": os.environ["PATH"], "RUSTC": "rustc", "KRUSTFLAGS": f"--sysroot={self.missing}" })
|
||||
self.assertIn("Source code for the 'core' standard library could not be found", result.stderr)
|
||||
|
||||
def test_rustc_src_core_rustlibsrc(self):
|
||||
result = self.run_script(self.Expected.FAILURE, { "RUST_LIB_SRC": self.missing })
|
||||
self.assertIn("Source code for the 'core' standard library could not be found", result.stderr)
|
||||
|
||||
def test_success_cc_unknown(self):
|
||||
result = self.run_script(self.Expected.SUCCESS_WITH_EXTRA_OUTPUT, { "CC": self.missing })
|
||||
self.assertIn("unknown C compiler", result.stderr)
|
||||
|
||||
def test_success_cc_multiple_arguments_ccache(self):
|
||||
clang = self.generate_clang(f"""Ubuntu clang version {self.llvm_default_version}-1ubuntu1
|
||||
Target: x86_64-pc-linux-gnu
|
||||
Thread model: posix
|
||||
InstalledDir: /usr/bin
|
||||
""")
|
||||
result = self.run_script(self.Expected.SUCCESS, { "CC": f"{clang} clang" })
|
||||
|
||||
def test_success_rustc_version(self):
|
||||
for rustc_stdout in (
|
||||
f"rustc {self.rustc_default_version} (a8314ef7d 2022-06-27)",
|
||||
f"rustc {self.rustc_default_version}-dev (a8314ef7d 2022-06-27)",
|
||||
f"rustc {self.rustc_default_version}-1.60.0 (a8314ef7d 2022-06-27)",
|
||||
):
|
||||
with self.subTest(rustc_stdout=rustc_stdout):
|
||||
rustc = self.generate_rustc(rustc_stdout)
|
||||
result = self.run_script(self.Expected.SUCCESS, { "RUSTC": rustc })
|
||||
|
||||
def test_success_bindgen_version(self):
|
||||
for bindgen_stdout in (
|
||||
f"bindgen {self.bindgen_default_version}",
|
||||
f"bindgen {self.bindgen_default_version}-dev",
|
||||
f"bindgen {self.bindgen_default_version}-0.999.0",
|
||||
):
|
||||
with self.subTest(bindgen_stdout=bindgen_stdout):
|
||||
bindgen = self.generate_bindgen_version(bindgen_stdout)
|
||||
result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
|
||||
|
||||
def test_success_bindgen_libclang(self):
|
||||
for stderr in (
|
||||
f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} (https://github.com/llvm/llvm-project.git 4a2c05b05ed07f1f620e94f6524a8b4b2760a0b1) [-W#pragma-messages], err: false",
|
||||
f"/home/jd/Documents/dev/kernel-module-flake/linux-6.1/outputs/dev/lib/modules/6.1.0-development/source/scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} [-W#pragma-messages], err: false",
|
||||
f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} (Fedora 13.0.0-3.fc35) [-W#pragma-messages], err: false",
|
||||
f"""
|
||||
/nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1-p16/bin/bash: warning: setlocale: LC_ALL: cannot change locale (c)
|
||||
scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} [-W#pragma-messages], err: false
|
||||
""",
|
||||
f"""
|
||||
/nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1.0-p16/bin/bash: warning: setlocale: LC_ALL: cannot change locale (c)
|
||||
/home/jd/Documents/dev/kernel-module-flake/linux-6.1/outputs/dev/lib/modules/6.1.0-development/source/scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} (Fedora 13.0.0-3.fc35) [-W#pragma-messages], err: false
|
||||
"""
|
||||
):
|
||||
with self.subTest(stderr=stderr):
|
||||
bindgen = self.generate_bindgen_libclang(stderr)
|
||||
result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
|
||||
|
||||
def test_success_clang_version(self):
|
||||
for clang_stdout in (
|
||||
f"clang version {self.llvm_default_version} (https://github.com/llvm/llvm-project.git 4a2c05b05ed07f1f620e94f6524a8b4b2760a0b1)",
|
||||
f"clang version {self.llvm_default_version}-dev",
|
||||
f"clang version {self.llvm_default_version}-2~ubuntu20.04.1",
|
||||
f"Ubuntu clang version {self.llvm_default_version}-2~ubuntu20.04.1",
|
||||
):
|
||||
with self.subTest(clang_stdout=clang_stdout):
|
||||
clang = self.generate_clang(clang_stdout)
|
||||
result = self.run_script(self.Expected.SUCCESS, { "CC": clang })
|
||||
|
||||
def test_success_real_programs(self):
|
||||
for cc in ["gcc", "clang"]:
|
||||
with self.subTest(cc=cc):
|
||||
result = self.run_script(self.Expected.SUCCESS, {
|
||||
"PATH": os.environ["PATH"],
|
||||
"RUSTC": "rustc",
|
||||
"BINDGEN": "bindgen",
|
||||
"CC": cc,
|
||||
})
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user